- March 18, 2024
- 5 min read
- 266
- 2K
Golang: Goroutine Leak là gì? Làm thế nào để tìm và xử lý nó.
Goroutine Leak Detector
Context
Trước khi đọc bài viết này hãy giả sử một tình huống rằng. Hôm nay ngoài trời đang nắng 40 độ C, trước khi đi làm bạn vào tắm cái cho nó mát, xong bật điều hoà và ngồi lướt tóp tóp chờ tóc khô rồi đi làm. Khi đi làm bạn lại quên tắt nước, tắt điều hoà. Và cái kết là thế nào thì bạn hiểu rồi đó, bạn sẽ phải trả giá bằng tiền điện và tiền nước cho khoản rò rỉ đó mặc dù nó không giúp ích được gì cho bạn. Trong phần mềm cũng vậy, dò rỉ bộ nhớ khiến ứng dụng bị hết bộ nhớ và giảm hiệu suất của ứng dụng.
Rò rỉ bộ nhớ có thể là mối lo ngại đáng kể trong bất kỳ ứng dụng phần mềm nào, kể cả những ứng dụng được viết bằng Golang. Rò rỉ bộ nhớ xảy ra khi một chương trình vô tình giữ lại bộ nhớ không còn cần thiết, dẫn đến tài nguyên hệ thống cạn kiệt dần. Theo thời gian, những rò rỉ này có thể làm giảm hiệu suất và thậm chí khiến ứng dụng bị treo.
Làm thế nào một Goroutine có thể xảy ra Leak?
Goroutine
leak được hiểu là khi mộtgoroutine
is được bắt đầu nhưng không bao giờ được hoàn thành. Vì mỗigoroutine
chiếm một lượng bộ nhớ hữu hạn nên việc rò rỉ goroutine sẽ dẫn đến rò rỉ bộ nhớ và quá trình hết bộ nhớ.
Rò rỉ bộ nhớ xảy ra khi bộ nhớ được phân bổ không được chương trình giải phóng hoặc giải phóng đúng cách. Trong Golang, trình thu gom rác (GC) quản lý việc cấp phát bộ nhớ và tự động lấy lại bộ nhớ không sử dụng. Tuy nhiên, rò rỉ bộ nhớ vẫn có thể xảy ra do cách sử dụng không chính xác, chu kỳ tham chiếu hoặc tài nguyên bên ngoài không được giải phóng đúng cách.
Vậy nguyên nhân nào để một goroutine
không thể kết thúc được:
- Một
gorountine
đang chờ để đọc từ mộtchannel
nhưng dữ liệu không bao giờ được gửi đến - Một goroutine cố gắng ghi vào một
channel
nhưng lại bị chặn vì dữ liệu hiện có không bao giờ được đọc (buffered channel
)
Ví dụ
func search() []string {
result := make(chan []string)
for i := 0; i < 3; i++ {
go func() {
result <- get()
}()
}
return <-result
}
func get() []string {
time.Sleep(time.Duration(rand.Intn(10) * int(time.Millisecond)))
return []string{"hello", "how"}
}
Mấu chốt của vấn đề là hàm search
chỉ lấy dữ liệu đầu tiên và trả về dữ liệu cho người gọi trong khi con goroutine
khác đã bắt đầu hoàn thành nghiêm túc các tìm kiếm của chúng và cố gắng ghi chúng vào channel
nhất định
Làm thế nào để có thể phát hiện được rò rỉ bộ nhớ?
Một số package
của bên thứ ba được thiết kế đặc biệt để phát hiện rò rỉ bộ nhớ trong ứng dụng Golang có thể tự động hóa quy trình. Một số tùy chọn phổ biến bao gồm
go-torch: This tool profiles memory and CPU usage to help identify bottlenecks and memory leaks.
golangci-lint: It offers a comprehensive set of linters, including memory leak detection, which can be integrated into your CI/CD pipeline.
heapster: This tool tracks memory allocations and provides detailed insights into memory usage patterns.
uber-go/goleak: GitHub - uber-go/goleak: Goroutine leak detector
zimmski/go-leak: GitHub - zimmski/go-leak: Detect all kinds of leaks in Go