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=1groups stacks with counts, anddebug=2gives full dumps showing how long each goroutine has waited, for example[chan send, 42 minutes]. Take a diff of two profiles withgo tool pprof -base. SIGQUIT(Ctrl-\) dumps all goroutine stacks.GOTRACEBACK=allincludes them on crashes.- Use
runtime/traceand 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
- Q161What does runtime.LockOSThread do and when is it needed?
- Q162What is a goroutine leak? Give common causes.
- Q164How many goroutines can a Go program run? How do you bound them?
- Q165Why doesn't Go expose goroutine IDs, and how do you "kill" a goroutine?
- Q166What happens when main returns while other goroutines are still running? What does this print?
- Q167What happens if a goroutine panics? Can the parent recover it?