Detectando Goroutine Leaks no Go 1.27

A partir do Go 1.27, podemos finalmente detectar goroutine leaks de forma nativa e sem precisar de flags experimentais. Neste post, vamos entender como funciona esse novo perfil de detecção e como usá-lo na prática para evitar um dos bugs mais difíceis de rastrear em aplicações Go.

O que é um Goroutine Leak?

Um goroutine leak ocorre quando uma goroutine fica bloqueada indefinidamente em alguma primitiva de concorrência (channels, sync.Mutex, sync.Cond, etc.) e nunca consegue ser desbloqueada. Diferente de uma goroutine que apenas dorme, uma goroutine em leak está prisioneira — nenhum outro código pode libertá-la.

Isso é um problema sério porque goroutines consomem memória. Se sua aplicação cria milhares delas sem desbloqueá-las, você terá vazamento de memória gradual e difícil de diagnosticar.

Como o Runtime Detecta Leaks

O Go 1.27 usa uma abordagem elegante baseada no garbage collector:

  1. O runtime executa um ciclo de GC (Garbage Collector)
  2. Para cada goroutine, verifica se ela está bloqueada em uma primitiva de concorrência
  3. Verifica se aquela primitiva é alcançável a partir de alguma goroutine que possa ser desbloqueada
  4. Se a primitiva for inalcançável, a goroutine está vazando

Esse mecanismo funciona porque se uma primitiva é inalcançável, ninguém pode sinalizar para ela, portanto a goroutine nunca acordará.

Usando o Perfil goroutineleak

No Go 1.27, você não precisa mais da flag GOEXPERIMENT=goleakleak. O perfil goroutineleak está disponível nativamente no pacote runtime/pprof.

Acessando o Perfil via Código

package main

import (
	"os"
	"runtime/pprof"
)

func main() {
	// ... seu código ...

	// Salvar o perfil em um arquivo
	f, err := os.Create("goroutineleak.prof")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	err = pprof.Lookup("goroutineleak").WriteTo(f, 0)
	if err != nil {
		panic(err)
	}
}

Via HTTP Endpoint

Se sua aplicação expõe o pacote net/http/pprof, o endpoint /debug/pprof/goroutineleak fica automaticamente disponível:

curl http://localhost:6060/debug/pprof/goroutineleak > goroutineleak.prof
go tool pprof goroutineleak.prof

Exemplo Prático: Detectando um Leak

Considere este código que cria um leak:

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int) // canal sem buffer

	// Esta goroutine fica bloqueada esperando receber do canal
	go func() {
		fmt.Println(<-ch)
	}()

	// Simula o resto da aplicação
	time.Sleep(2 * time.Second)
	// A goroutine nunca receberá nada — leak!
}

Rodando com o novo perfil:

# Compilar
go build -o myapp .

# Executar
./myapp &

# Capturar o perfil
curl http://localhost:6060/debug/pprof/goroutineleak > leak.prof

# Analisar
go tool pprof leak.prof
# Você verá a goroutine bloqueada no canal, com a stack trace completa

Quando Usar na Prática

O perfil goroutineleak é particularmente útil em:

  • Testes automatizados: Adicione verificações antes e depois de testes para garantir que nenhuma goroutine vazou
  • Debugging em produção: Capture perfis periodicamente para monitorar leaks
  • Code reviews: Verifique se handlers HTTP e workers limpam suas goroutines corretamente

Conclusão

O Go 1.27 traz uma ferramenta nativa e eficiente para detectar goroutine leaks — um dos bugs mais insidiosos em aplicações concorrentes. Agora você pode identificar e corrigir esses problemas com precisão, sem depender de flags experimentais ou ferramentas externas.

Se você trabalha com Go e concorrência, é recomendável integrar essa verificação em seus testes e monitoramento.

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.

* indicates required

Deixe uma resposta