Nesse post, vamos entender uma das otimizações mais interessantes das últimas versões do Go: a capacidade do compilador de alocar slices na stack em mais cenários — sem você precisar mudar uma linha de código.
O problema: heap allocation e o GC
Em Go, cada variável precisa ser alocada em algum lugar: na stack (pilha) ou na heap. A stack é rápida — alocar e liberar memória lá é praticamente gratuito. Já a heap é gerenciada pelo garbage collector, o que tem um custo: quanto mais você aloca no heap, mais trabalho o GC tem para fazer.
O compilador do Go usa uma técnica chamada escape analysis para decidir onde cada variável vai parar. Se ele consegue provar que uma variável não vai “escapar” do escopo da função, ele a coloca na stack. Caso contrário, ela vai para o heap.
O problema é que essa análise é conservadora: em muitos casos práticos, como acumular elementos em um slice dentro de um loop, o compilador não tinha certeza se o slice ia escapar ou não e alocava no heap por precaução.
O que mudou no Go 1.25
Go 1.25 introduziu uma otimização chamada alocação especulativa na stack. A ideia é simples: o compilador reserva um pequeno buffer de 32 bytes na stack antes de qualquer uso do slice.
Em tempo de execução:
- Se o slice couber nesse buffer (com base no tamanho estimado), ele usa a stack — zero alocação no heap.
- Se for maior, cai para a alocação normal no heap.
// Antes do Go 1.25: o compilador não sabia o tamanho e alocava no heap
func nomes() []string {
var result []string
result = append(result, "Alice")
result = append(result, "Bob")
return result
}
No Go 1.25, se o conteúdo couber nos 32 bytes reservados na stack, a alocação inicial acontece ali e só vai para o heap se necessário.
O que mudou no Go 1.26
Go 1.26 foi além. O compilador agora reconhece o padrão mais comum de uso de slices que é a acumulação com append em loops, e aplica a mesma estratégia de forma ainda mais ampla.
func processarItens(itens []string) []string {
var resultado []string
for _, item := range itens {
if len(item) > 3 {
resultado = append(resultado, item)
}
}
return resultado
}
Antes, esse padrão quase sempre ia para o heap desde o início. Agora, o compilador aloca um buffer na stack antes do loop começar, e os primeiros appends usam esse buffer sem nenhuma alocação no heap.
E quando o slice precisa escapar?
Quando o slice realmente precisa ir para o heap (por exemplo, quando é retornado ou capturado por uma closure), Go 1.26 usa uma função especial chamada runtime.move2heap. Ela verifica:
- Se o slice já está no heap: retorna como está, sem custo extra.
- Se está na stack: copia o conteúdo para o heap e retorna a nova referência.
Isso permite que o compilador sempre otimize pelo caso comum (stack), sabendo que o runtime vai lidar com a promoção para o heap quando necessário.
Por que isso importa na prática
Essas melhorias impactam diretamente o throughput de aplicações que:
- Fazem muitas operações com slices de tamanho variável
- Têm funções que acumulam resultados com
append - Precisam de baixa latência (menor pausa do GC)
O melhor: você não precisa fazer nada. Basta atualizar o toolchain para Go 1.25 ou 1.26 e recompilar. O compilador faz o trabalho por você.
Para verificar se uma alocação está na stack ou heap, você pode usar a flag -gcflags="-m":
go build -gcflags="-m" ./...
Procure por mensagens como does not escape, onde cada uma dessas é uma alocação que ficou fora do heap.
Conclusão
Go 1.25 e 1.26 trouxeram ganhos reais de performance para um dos padrões mais comuns em código Go: a acumulação de slices. Ao alocar especulativamente na stack e usar runtime.move2heap quando necessário, o compilador elimina pressão no GC sem exigir nenhuma mudança no seu código.
Esse é o tipo de otimização que mostra como a equipe do Go pensa: melhorar o caso comum sem quebrar compatibilidade e sem adicionar complexidade para o desenvolvedor.
Gostou do conteúdo?
- ✅ Inscreva-se na newsletter para receber mais dicas práticas sobre Go diretamente no seu e-mail!
- 🚀 Conheça a Imersão Golang e leve seus conhecimentos em Go para o próximo nível!
Faça parte da comunidade!
Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.