Goroutine Leak Detector
A program with intentional goroutine leaks. Run it, then visit http://localhost:6060/debug/pprof/goroutine?debug=1 to see the leaks pile up.
package main
import (
"fmt"
"net/http"
_ "net/http/pprof"
"time"
)
// ❌ Keep references alive so GC can't collect the leaked goroutines
var leakedChannels []chan string
// ❌ LEAK: channel is never read — goroutine blocks on send forever
func leakyWriter() {
ch := make(chan string)
leakedChannels = append(leakedChannels, ch) // prevents GC from collecting
go func() {
ch <- "hello" // blocks forever — nobody reads from ch
}()
}
// ❌ Keep references alive so GC can't collect the leaked goroutines
var leakedSignals []chan struct{}
// ❌ LEAK: channel is never closed — goroutine waits forever
func leakyListener() {
ch := make(chan struct{})
leakedSignals = append(leakedSignals, ch) // prevents GC from collecting
go func() {
<-ch // blocks forever — nobody closes or sends to ch
}()
}
func main() {
// Start pprof debug server
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
// Spawn leaks every second
go func() {
for {
leakyWriter() // ❌ leaks one goroutine per call
leakyListener() // ❌ leaks one goroutine per call
time.Sleep(time.Second)
}
}()
fmt.Println("leaking goroutines... visit http://localhost:6060/debug/pprof/goroutine?debug=1")
fmt.Println("refresh the browser to see the goroutine count grow")
fmt.Println("press Ctrl+C to stop")
select {} // block forever
}Run this and visit the pprof URL after 10 seconds. The first line tells you everything:
goroutine profile: total 59 ← first check
goroutine profile: total 69 ← few seconds later, growing
goroutine profile: total 171 ← keeps growing — you have a leakIf the total keeps climbing on every refresh, you're leaking goroutines. The rest of the output shows where they're stuck, but the first line is your quick health check.