Pilha ou heap em Go: como a alocação de struct afeta o GC

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que "apenas um struct" não é tão simples assim?

\n\n

Você já escreveu em Go e, olhando para seu programa, se perguntou: "Onde minha estrutura está morando agora? Na pilha rápida e aconchegante ou nos vastos domínios do heap sob a supervisão do coletor de lixo?". Essa questão atormenta não apenas iniciantes. No Stack Overflow, o tópico "Stack vs heap allocation of structs in Go, and how they relate to garbage collection" acumulou milhares de visualizações. E não é por acaso.

\n\n

A aparente simplicidade do Go esconde um mecanismo poderoso — o escape analysis. Este é um detetive do compilador que decide o destino de cada objeto. Sua decisão afeta o desempenho da sua aplicação e a carga no GC (Garbage Collector). Neste tutorial passo a passo, vamos colocar o chapéu de detetive e entender:

\n
    \n
  • Por que uma estrutura vive na pilha e outra no heap?
  • \n
  • Como ponteiros e closures quebram a paz da pilha.
  • \n
  • Como esse conhecimento ajuda você a escrever código rápido e eficiente em Go.
  • \n
\n\n

Pilha vs Heap: Breve introdução

\n

Antes de mergulhar no código, vamos refrescar a teoria. Não se preocupe, será rápido e direto.

\n\n

Pilha (Stack)

\n
    \n
  • Velocidade: Relâmpago. Alocar memória é apenas mover o ponteiro para baixo.
  • \n
  • Ciclo de vida: Estritamente ligado à chamada da função. Vive enquanto a função está em execução.
  • \n
  • Gerenciamento: Automático. "Desaparece" ao sair da função.
  • \n
  • Tamanho: Pequeno e fixo por goroutine (geralmente 2-4 KB).
  • \n
\n\n

Heap

\n
    \n
  • Velocidade: Mais lento. Requer busca por um bloco de memória livre.
  • \n
  • Ciclo de vida: Enquanto houver referências ao objeto no programa. Pode sobreviver à função que o criou.
  • \n
  • Gerenciamento: Coletor de lixo (GC). Ele varre o heap, encontra objetos "mortos" e libera memória. Isso custa recursos de CPU.
  • \n
  • Tamanho: Grande (limitado apenas pela memória do SO).
  • \n
\n\n

A regra de ouro do Go: "Mantenha os dados na pilha o máximo possível". Isso reduz a carga no GC e acelera o programa.

\n\n

Escape Analysis: O detetive chefe do Go

\n

Em Go, você não escreve new ou malloc para indicar explicitamente onde colocar um objeto. O compilador decide isso por você. Esse processo é chamado de escape analysis (análise de escape).

\n

Ele analisa seu código e faz uma pergunta: "A referência a este objeto pode sair da função atual?".

\n
    \n
  • Se NÃO (objeto não escapa) → O objeto é colocado na pilha.
  • \n
  • Se SIM (objeto escapa) → O objeto é colocado no heap.
  • \n
\n\n

Vamos ver isso em ação.

\n\n

Exemplo 1: Estrutura segura na pilha

\n
package main\n\nimport "fmt"\n\ntype Point struct {\n    X, Y int\n}\n\n// createPoint cria um Point e o retorna por valor.\nfunc createPoint(x, y int) Point {\n    p := Point{X: x, Y: y}\n    return p // p é copiado no retorno\n}\n\nfunc main() {\n    p1 := createPoint(10, 20)\n    fmt.Println(p1)\n}\n
\n

Neste exemplo, p é uma variável local. Ela é criada dentro de createPoint e retornada por valor (copiada). A própria estrutura p não pode ser usada fora da função (sua cópia é usada em main). O escape analysis vê isso e, felizmente, coloca p na pilha. O GC descansa.

\n\n

Exemplo 2: Escape através de ponteiro

\n

Agora vamos mudar apenas uma linha — retornar um ponteiro.

\n
package main\n\nimport "fmt"\n\ntype Point struct {\n    X, Y int\n}\n\n// createPointer cria um Point e retorna um ponteiro para ele.\nfunc createPointer(x, y int) *Point {\n    p := P                

Blogs

Book Recommendations