How to Detect and Fix Go Goroutine Leaks
In this tutorial, you'll learn about How to Detect and Fix Go Goroutine Leaks. We cover key concepts, practical examples, and best practices.
Go goroutine leaks occur when goroutines exit without being cleaned up, staying blocked on channel operations, mutex locks, or I/O waits. These leaks cause memory growth and performance degradation over time.
Quick Fix
Wrong
func main() {
ch := make(chan int)
go func() {
<-ch // blocks forever, no one sends
}()
// Goroutine leaks
}
The goroutine waits on a channel that never receives a value. It stays in memory forever.
Right
func main() {
ch := make(chan int, 1)
ch <- 42
go func() {
val := <-ch
fmt.Println("Received:", val)
}()
time.Sleep(100 * time.Millisecond) // give goroutine time to run
}
Received: 42
Fix with context cancellation
func worker(ctx context.Context, ch <-chan int) {
for {
select {
case val := <-ch:
fmt.Println("Got:", val)
case <-ctx.Done():
fmt.Println("Worker stopping")
return
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
ch := make(chan int)
go worker(ctx, ch)
cancel() // stops the worker
}
Worker stopping
Detect leaks with runtime.NumGoroutine
func main() {
before := runtime.NumGoroutine()
// ... run code ...
after := runtime.NumGoroutine()
if after > before+1 {
fmt.Printf("Possible leak: %d goroutines\n", after-before)
}
}
Prevention
- Always use
context.WithCancelorcontext.WithTimeoutfor goroutine lifecycle management. - Use buffered channels to prevent sender blocks.
- Never let a goroutine block on a channel without a select with context or timeout.
- Monitor with
runtime.NumGoroutine()in health checks. - Test with
-raceandpprofgoroutine profiling.
DodaTech Tools
Doda Browser's Go profiling dashboard shows goroutine count over time with leak detection alerts. DodaZIP archives goroutine stack dumps for analysis. Durga Antivirus Pro monitors goroutine counts in production and alerts on abnormal growth.
Common Mistakes with goroutine leak detect
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
- Non-exhaustive pattern matches that compile with warnings then crash at runtime
- Misunderstanding that
Stringis[Char]with poor performance for large text operations
These mistakes appear frequently in real-world GO code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro