Go

How do you detect and debug goroutine leaks in production and tests?

Question 163HardGo 1.22 to 1.25

In production:

  • Export runtime.NumGoroutine() as a metric and alert when it grows steadily.
  • Use net/http/pprof. /debug/pprof/goroutine?debug=1 groups stacks with counts, and debug=2 gives full dumps showing how long each goroutine has waited, for example [chan send, 42 minutes]. Take a diff of two profiles with go tool pprof -base.
  • SIGQUIT (Ctrl-\) dumps all goroutine stacks. GOTRACEBACK=all includes them on crashes.
  • Use runtime/trace and the flight recorder (trace.FlightRecorder, Go 1.25) to capture the last few seconds of execution when an anomaly happens.
  • Go 1.26 adds an experimental goroutine leak profile (GOEXPERIMENT=goroutineleakprofile). It uses the GC to find goroutines blocked on concurrency primitives that nothing else can reach.

In tests: use go.uber.org/goleak. Use testing/synctest (GA in Go 1.25) for deterministic tests of concurrent code: synctest.Test fails if goroutines inside the bubble are left deadlocked.

func TestMain(m *testing.M) { goleak.VerifyTestMain(m) }

func TestWorker(t *testing.T) {
	defer goleak.VerifyNone(t)
	// ...
}

More on Goroutines & the Scheduler

All 35 Goroutines & the Scheduler questions