Goroutine leak là goroutine được tạo ra nhưng không bao giờ kết thúc — nó chặn vĩnh viễn trên một thao tác không bao giờ hoàn tất. Goroutine đó giữ luôn stack, biến nó tham chiếu và cả connection nó đang cầm, nên GC không thu hồi được gì.
Bốn dạng phổ biến:
1. Gửi mà không còn ai nhận — dạng hay gặp nhất. Hàm cha return sớm (timeout, lỗi validate) nên không đọc kết quả, goroutine con chặn mãi ở lệnh gửi.
2. Nhận từ channel không ai đóng — worker range trên channel mà producer thoát sớm và quên close.
3. Không nghe tín hiệu hủy — vòng lặp không có nhánh <-ctx.Done(), nên hủy request cũng không dừng được nó.
4. Chặn trên lock / WaitGroup — wg.Wait() không bao giờ về vì thiếu một lần Done.
func fetch(ctx context.Context, url string) (string, error) {
ch := make(chan string) // unbuffered: the sender needs a receiver
go func() { ch <- httpGet(url) }()
select {
case v := <-ch:
return v, nil
case <-ctx.Done():
return "", ctx.Err() // nobody will ever read ch: the goroutine leaks
}
}Sửa dạng này chỉ cần make(chan string, 1) — bên gửi luôn đặt được giá trị rồi thoát, dù không ai đọc.
Nguyên tắc chung: không tạo goroutine nếu chưa biết nó sẽ dừng bằng cách nào.