Go

How would you detect and diagnose a goroutine leak?

Question 372MediumGo 1.22 to 1.25

Symptom: runtime.NumGoroutine() (export it as a metric) grows monotonically along with memory. Diagnose with the goroutine profile:

curl -s 'http://127.0.0.1:6060/debug/pprof/goroutine?debug=1' | head -50
# debug=1: grouped by identical stack with counts
# debug=2: every goroutine, full stack, with wait reason and duration
go tool pprof -http=:8080 http://127.0.0.1:6060/debug/pprof/goroutine

Look for a large count parked on the same line, e.g. chan send or select for "35 minutes". Classic culprit:

func firstResult(ctx context.Context, urls []string) string {
    ch := make(chan string) // BUG: unbuffered; losers block forever
    for _, u := range urls {
        go func() { ch <- fetch(u) }()
    }
    return <-ch
}
// Fix: make(chan string, len(urls)), or select on ctx.Done() in the sender.

In tests, catch leaks with go.uber.org/goleak (defer goleak.VerifyNone(t) or in TestMain), or with testing/synctest, which fails if goroutines in the bubble are left deadlocked. Go 1.26 also adds an experimental goroutineleak profile that uses the GC to find goroutines blocked on unreachable channels. Interviewer wants: every goroutine must have a clear exit path — context cancellation, closed channel, or buffered send.

More on Performance, Profiling & Testing

All 38 Performance, Profiling & Testing questions