O Runtime do Go: Scheduler, GC e Memória
Seu código Go compila para código de máquina, mas a execução não é em metal bruto.
Busque em todas as páginas da documentação
Seu código Go compila para código de máquina, mas a execução não é em metal bruto.
Um runtime embutido agenda goroutines, gerencia a memória do heap, executa o coletor de lixo e coordena chamadas de sistema para que milhares de tarefas leves compartilhem um pequeno pool de threads de forma eficiente.
Noções Básicas do Runtime coleta trechos executáveis para runtime.MemStats e métricas de GC.
Artigos irmãos cobrem marcação tri-color, Green Tea GC no Go 1.26, análise de escape, ajustes de parâmetros, crescimento de pilha, padrões de vazamento e fluxos de trabalho de produção.
pprof.GOMAXPROCS.pprof, limites de memória de contêineres, análise de escape do compilador, métricas de observabilidade.Imagine um processo Go em execução como três subsistemas cooperativos.
Agendamento decide qual goroutine será executada em qual thread do sistema operacional.
Alocação distribui blocos de heap quando valores não podem viver na pilha.
Coleta de lixo rastreia ponteiros vivos e libera memória de heap inacessível.
Goroutines não são threads do sistema operacional.
Cada goroutine começa com uma pequena pilha (na ordem de alguns KiB) que cresce sob demanda.
O runtime pausa goroutines que bloqueiam em canais, mutexes ou I/O para que a thread subjacente possa executar outro trabalho.
O modelo GPM nomeia as peças:
GOMAXPROCS define quantos Ps existem.
Isso limita quantas goroutines executam bytecode Go em paralelo em núcleos de CPU, não quantas goroutines você pode criar.
Objetos de heap são criados quando o compilador não consegue provar que a vida útil de um valor cabe em um frame de pilha.
Retornar um ponteiro para uma variável local, armazenar dados em interfaces ou mapas com vida útil desconhecida, ou capturar variáveis em closures que sobrevivem ao frame comumente forçam a alocação de heap.
O alocador organiza a memória em classes de tamanho dentro de páginas (tipicamente 8 KiB no heap Go).
O coletor de lixo é de rastreamento: ele caminha por ponteiros a partir de raízes (globais, pilhas, registradores) e marca objetos alcançáveis.
Objetos inacessíveis são varridos e sua memória é devolvida ao alocador.
O scheduler e o GC interagem constantemente.
Quando o GC precisa inspecionar pilhas ou impor uma fase stop-the-world (STW), as goroutines são pausadas brevemente.
A maior parte do trabalho de marcação é executada concorrentemente com seu código via write barriers que rastreiam atualizações de ponteiros enquanto a fase de marcação prossegue.
O algoritmo clássico é tri-color mark-sweep:
white = ainda não visitado (lixo candidato)
gray = visitado, filhos não totalmente escaneados
black = visitado, filhos totalmente escaneados
O runtime mantém o invariante de que nenhum objeto preto aponta para um objeto branco sem um objeto cinza entre eles.
O controle (Pacing) decide quando o próximo ciclo de GC começa.
Por padrão, GOGC=100 significa que o heap pode crescer para aproximadamente 100% da memória viva desde a última coleta antes de acionar outro ciclo.
GOMEMLIMIT (Go 1.19+) adiciona um limite de memória suave para que o coletor controle o ritmo de forma mais agressiva à medida que o RSS se aproxima de um limite - crítico em pods Kubernetes com limites rígidos de cgroup.
O Go 1.26 torna o Green Tea GC o coletor padrão.
Em vez de uma lista de trabalho por objeto que salta aleatoriamente pelo heap, o Green Tea agrupa o escaneamento por página, melhorando a localidade do cache da CPU e permitindo loops de escaneamento vetorizados em hardware x86 moderno.
Opte por sair em tempo de compilação com GOEXPERIMENT=nogreenteagc se precisar do caminho de marcação legado para comparação.
// Ilustrativo: inspecionar o estado do runtime a partir do código da aplicação
import "runtime"
func snapshot() {
var ms runtime.MemStats
runtime.ReadMemStats(&ms)
_ = ms.HeapAlloc // bytes em uso no heap
_ = ms.NumGC // ciclos de GC concluídos
_ = ms.PauseTotalNs
}A análise de escape é executada em tempo de compilação.
Quando o endereço de um valor não pode escapar de sua função, o compilador aloca na pilha - sem envolvimento do GC, liberado ao retornar.
Quando ele escapa, cada alocação se torna trabalho para o GC.
Serviços de produção instrumentam o runtime em vez de adivinhar.
Exporte runtime/metrics ou analise MemStats para HeapAlloc, HeapInuse, StackInuse, NumGoroutine e PauseNs por ciclo.
Combine perfis de heap de net/http/pprof com rastreamentos de alocação para encontrar chamadas make quentes ou boxing não intencional.
O dimensionamento de contêineres deve levar em conta a memória da pilha (contagem de goroutines vezes a pilha média) mais o objetivo do heap impulsionado por GOGC e GOMEMLIMIT.
Um serviço com milhões de goroutines em espera pode usar RSS substancial, mesmo quando os objetos de heap são modestos.
| Botão | Força | Fraqueza | Melhor Ajuste |
|---|---|---|---|
| GC Padrão (Green Tea) | Menor CPU de marcação em heaps típicos | Alguns heaps irregulares veem menos benefício | Serviços Go 1.26+ sem necessidades especiais de GC |
Ajuste de GOGC | Troca CPU por memória ou vice-versa | Ajuste incorreto aumenta picos de latência | Workers de lote vs APIs sensíveis à latência |
GOMEMLIMIT | Respeita limites de cgroup | Limite suave; OOM ainda possível sob picos | Kubernetes, serverless, hosts compartilhados |
runtime.GC() Manual | Força a coleta para testes | Prejudica a produção em latência se mal utilizado | Apenas benchmarks, snapshots de diagnóstico |
sync.Pool | Reutiliza buffers transitórios | Entradas do pool podem ser limpas no GC | Buffers de serialização, espaço de rascunho de codificação |
Finalizadores (runtime.SetFinalizer) executam código arbitrário durante o GC e são fáceis de usar incorretamente.
Prefira métodos Close() explícitos e defer para recursos.
Pausas STW são curtas no Go moderno, mas ainda importam para a latência de cauda.
Meça com GODEBUG=gctrace=1 durante testes de carga, não apenas em desenvolvimento.
GOMAXPROCS goroutines executam código Go na CPU simultaneamente; o excesso de goroutines fica na fila ou em espera.Scheduler de goroutines, alocador de memória, coletor de lixo, gerenciamento de pilha, suporte a reflexão, mecanismos de pânico/defer e interfaces para o sistema operacional (chamadas de sistema, sinais, timers).
Seu binário o vincula automaticamente - não há uma instalação separada no estilo JVM.
Threads do sistema operacional são pesadas (pilhas na escala de MB, agendamento no kernel).
Goroutines são baratas (pilhas na escala de KiB, agendamento em espaço do usuário).
Ms executam goroutines; Ps distribuem o trabalho entre os núcleos; operações de bloqueio colocam goroutines em espera sem bloquear threads desnecessariamente.
Quando o crescimento do heap cruza o gatilho de GC derivado de GOGC, o tamanho do heap vivo e opcionalmente GOMEMLIMIT.
Você também pode acionar manualmente com runtime.GC() para diagnóstico.
Uma pequena peça de código que o compilador insere quando ponteiros são atualizados durante a marcação concorrente.
Ela notifica o GC para que ele não perca objetos vivos enquanto o heap é mutado sob goroutines em execução.
O Green Tea se tornou o coletor padrão.
Ele escaneia por página de heap em vez de objetos individuais na lista de trabalho, melhorando a localidade e reduzindo a CPU da fase de marcação para muitas cargas de trabalho.
Compile com GOEXPERIMENT=nogreenteagc para usar o algoritmo legado.
Indiretamente.
Mais Ps significam mais workers de marcação paralelos durante o GC, mas também mais CPU mutadora concorrente competindo por cache e largura de banda de memória.
Combine GOMAXPROCS com os limites de CPU do contêiner.
Dados vivos permanecem alocados.
HeapAlloc após o GC reflete objetos alcançáveis mais fragmentação do alocador e spans não utilizados ainda não devolvidos ao sistema operacional.
HeapIdle e HeapReleased mostram memória devolvida ao sistema operacional ao longo do tempo.
Pilhas de goroutine não são objetos coletados pelo GC, mas ainda consomem RSS.
Recursão profunda ou milhões de goroutines aumentam StackInuse em MemStats independentemente da pressão do heap.
Não em compilações de produção.
GOGC=off existe para benchmarking especializado, mas é inseguro para serviços de longa duração porque o heap cresce sem limites.
Alocações de pilha pulam o heap inteiramente.
Cada escape força uma alocação que o GC eventualmente deve rastrear e varrer.
Alta taxa de alocação é o principal impulsionador do tempo de CPU do GC.
process_resident_memory_bytes, go_memstats_heap_alloc_bytes, go_memstats_gc_cpu_fraction, contagem de goroutines e latência de cauda durante as fases de GC.
Alerta sobre crescimento sustentado, não picos únicos após a implantação.
Compilações hospedadas em TinyGo e wazero usam runtimes diferentes com trade-offs distintos de GC e agendamento.
Esta seção foca no runtime padrão do cmd/compile para binários de servidor Linux/macOS/Windows.
MemStats e contagem de goroutinesVersões de Pilha: Esta página foi escrita para Go 1.26.x (Green Tea GC padrão, go fix modernizers - verifique o patch na compilação), chi (mais recente - verifique na compilação), gin (mais recente - verifique na compilação), echo (mais recente - verifique na compilação), google.golang.org/grpc (mais recente - verifique na compilação), sigs.k8s.io/controller-runtime (mais recente - verifique na compilação), kubebuilder (mais recente - verifique na compilação), tinygo (mais recente - verifique os alvos de placa na compilação), wazero (mais recente - verifique na compilação) e golangci-lint (conjunto de linters mais recente - verifique na compilação).
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026