goroutineleak pprof Profile
goroutineleak pprof Profile
Thesis
Go 1.27’s goroutineleak pprof profile reports goroutines that are blocked forever on unreachable synchronisation objects — the classic “lost channel” leak. It is distinct from the plain goroutine profile, which shows everything currently runnable or blocked.
Mental model
go func() {
c := make(chan struct{})
go func() { <-c }() // blocked forever
// c goes out of scope → unreachable
}()
pprof: goroutineleak → stacks of such unreachable blocked Gs
The profile writer triggers GC / leak detection before snapshotting (writeGoroutineLeak in runtime/pprof).
Worked example
Verified against Go 1.27.2 sources (src/runtime/pprof/pprof.go).
/* leakprobe.go */
package main
import (
"fmt"
"net/http"
_ "net/http/pprof"
"runtime"
"time"
)
func main() {
go func() {
c := make(chan struct{})
go func() { <-c }()
// c not retained → inner G is a leak candidate after GC
}()
time.Sleep(50 * time.Millisecond)
runtime.GC()
fmt.Println("NumGoroutine", runtime.NumGoroutine())
go http.ListenAndServe("127.0.0.1:6061", nil)
fmt.Println("fetch: curl -s 'http://127.0.0.1:6061/debug/pprof/goroutineleak?debug=1'")
time.Sleep(time.Hour)
}go run leakprobe.go
# elsewhere:
curl -s 'http://127.0.0.1:6061/debug/pprof/goroutineleak?debug=1' | headProfile registration on this GOROOT includes:
name: "goroutineleak"
# writeGoroutineLeak first invokes a GC cycle that performs goroutine leak detection
Trap
Treating every blocked goroutine as a leak. A server goroutine blocked on Accept is healthy. goroutineleak is specifically about unreachable sync objects. Also: taking the profile forces GC work — do not scrape it at huge QPS without budgets.
Boring rule
When diagnosing “goroutine count only goes up”, compare goroutine vs goroutineleak. Fix leaks by retaining shutdown signals (context cancel, close of a done channel) rather than dropping the only sender/receiver.
Try this
go tool pprof http://.../debug/pprof/goroutineleakandlistthe offending frames.- Deliberately leak, confirm the profile, then fix with
context.WithCanceland re-check. - Read
TestGoroutineLeakProfileConcurrencyin$GOROOT/src/runtime/pprof/pprof_test.go.
Sources
- Go 1.27
src/runtime/pprof/pprof.go(goroutineleakprofile) net/http/pprof- Go release notes for 1.27 diagnostics