Se você já trabalhou com Go em produção, provavelmente já enfrentou aquele cenário: goroutines que ficam presas para sempre, consumindo memória sem nunca terminar. O Go 1.26 trouxe uma ferramenta experimental que ataca esse problema direto na raiz: o goroutine leak profile.
O que são goroutine leaks?
Uma goroutine leak acontece quando uma goroutine fica bloqueada permanentemente — esperando em um channel, mutex ou condition variable que nunca vai ser desbloqueado. Na prática, é memória e recursos que ficam presos sem possibilidade de liberação.
O cenário mais clássico: você cria uma goroutine que escreve em um channel sem buffer, mas a função que deveria ler esse channel retorna antes por conta de um erro. A goroutine fica ali, bloqueada para sempre.
func processData(ctx context.Context) error {
ch := make(chan result)
go func() {
// Essa goroutine vai ficar presa se ninguém ler do channel
ch <- doHeavyWork()
}()
select {
case <-ctx.Done():
return ctx.Err() // retorna sem ler ch — goroutine vazou
case r := <-ch:
return r.err
}
}
No exemplo acima, se o contexto for cancelado antes de doHeavyWork() terminar, a goroutine produtora fica bloqueada eternamente no ch <-. Isso é um leak.
Como o novo profile funciona
O goroutine leak profile usa a fase de marcação do garbage collector para identificar goroutines que estão bloqueadas em primitivas de concorrência — channels, mutexes, condition variables — que são inalcançáveis por qualquer goroutine em execução.
A lógica é simples: se nenhuma goroutine ativa consegue acessar o channel ou mutex no qual uma goroutine está bloqueada, essa goroutine nunca vai ser desbloqueada. Ou seja, é um leak.
O mais interessante é que essa detecção não adiciona overhead ao runtime enquanto não estiver sendo usada. Quando você solicita o profile, o runtime dispara um ciclo especial de GC focado na detecção de leaks.
Como habilitar
Por enquanto, o recurso é experimental. Para ativá-lo, você precisa compilar com a flag GOEXPERIMENT:
GOEXPERIMENT=goroutineleakprofile go build -o myapp ./cmd/myapp
Depois de compilado, o profile fica disponível de duas formas.
Via código com runtime/pprof
import "runtime/pprof"
profile := pprof.Lookup("goroutineleak")
if profile != nil {
f, _ := os.Create("goroutine_leaks.prof")
defer f.Close()
profile.WriteTo(f, 0)
}
Via endpoint HTTP
Se você já usa net/http/pprof (e deveria), o endpoint fica disponível automaticamente:
GET /debug/pprof/goroutineleak
Basta acessar com go tool pprof:
go tool pprof http://localhost:6060/debug/pprof/goroutineleak
Na prática: encontrando leaks
Imagine que você tem um serviço em produção e suspeita de goroutine leaks. O fluxo é direto:
- Compile com
GOEXPERIMENT=goroutineleakprofile - Deploy normalmente
- Após algum tempo de execução, colete o profile via endpoint HTTP
- Analise com
go tool pprof
go tool pprof -http=:8080 http://localhost:6060/debug/pprof/goroutineleak
O output vai mostrar as goroutines que o runtime identificou como leaks, com o stack trace completo de onde elas foram criadas. Isso facilita muito encontrar a origem do problema.
Limitações
O profile tem uma limitação importante: ele não consegue detectar leaks causados por bloqueios em primitivas de concorrência que são alcançáveis via variáveis globais ou variáveis locais de goroutines que ainda estão rodando. Se o channel está armazenado em uma struct global, o runtime não tem como saber que ninguém vai ler dele.
Outro ponto: como é experimental, a API pode mudar. A expectativa é que o recurso seja habilitado por padrão no Go 1.27.
Resultados reais
A abordagem já foi validada em cenários reais pela equipe da Uber, que contribuiu com a implementação. Nos testes, o profile identificou entre 180 e 357 leaks distintos em mais de 3.000 suítes de teste, e encontrou 3 tipos diferentes de leak em um serviço de produção — com 252 ocorrências em apenas 24 horas.
Conclusão
Goroutine leaks são um problema silencioso que pode degradar a performance do seu serviço ao longo do tempo. Até agora, detectá-los dependia de ferramentas externas ou análise manual. Com o goroutine leak profile do Go 1.26, o próprio runtime cuida disso pra você — aproveitando o trabalho que o GC já faz.
Se você trabalha com Go em produção, vale muito a pena testar. Compile com GOEXPERIMENT=goroutineleakprofile, rode por algumas horas, e veja se o seu serviço tem goroutines esquecidas por aí.
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.