Original link: https://sund.site/posts/2023/goroutine-leak/
Recently, when I was investigating memory leaks in Go language at work, I found this blog written by Uber , which shared several common goroutine memory leak patterns, so I sorted out goroutine-related issues, hoping that more people will find this post Articles to help you quickly locate memory leaks.
Causes of Goroutine memory leaks
Memory leaks in Go are often caused by incorrect use of goroutines and channels. For example, the following situations:
- Open a connection (such as gRPC) in a goroutine but forget to close it
- The global variable object in the goroutine is not released
- The channel is read in the goroutine, but it is blocked without writing to the end
- Writing to an unbuffered channel in a goroutine, but blocked because the channel’s read end was closed by another goroutine
- Writing to a buffered channel in a goroutine, but the channel buffer is full
These kinds of situations are usually mixed in the logic of complex code, and it is difficult to debug and find problems. Therefore, the following patterns that are most prone to problems in daily work are derived.
Common Goroutine memory leak patterns
Premature Function Return / function returns prematurely
A goroutine is about to write to a channel, but the other end exits unexpectedly causing the code that the channel reads to not execute.
|
|
In the code, the main process returns at if a > 0
, which causes the channel to be blocked because it cannot be written.
One way to solve this problem is to convert the unbuffered channel into a channel with a buffer size of 1.
|
|
A buffered channel will not block even if there is no read operation.
The Timeout Leak / Timeout Leak
This is a problem encountered in our work, and it is often used when an asynchronous operation that may timeout needs to be performed.
|
|
In this code, once the timeoutOption operation times out, select will be notified, and then the program exits, so the operation of goroutine writing done is blocked and cannot exit.
The solution is the same as the previous mode, using a buffered channel instead of an unbuffered channel.
The NCast Leak / Multi-terminal read and write leak
This can happen if the channel has only one read end, but multiple write ends.
|
|
This situation also applies to the situation of “multiple write ends and one read end”. The solution is to set the channel to the same number of buffers as the number of writes or reads.
|
|
Channel Iteration Misuse / Channel Iteration Misuse
Go supports a feature “Range over channels” , which can be used to read the content of the channel in a loop.
But once the content cannot be read, the range will wait for the channel to be written, and if the range happens to be inside the goroutine, the goroutine will be blocked.
|
|
The way to solve this problem is to manually define the closed channel.
|
|
In this way, after the WaitGroup is all over, the main program will close the channel, so that the range inside the asynchronous goroutine exits the loop waiting.
summary
Goroutine memory leaks are the most prone to memory leaks in the Go language, and they are usually accompanied by incorrect use of goroutines and channels. The special usage of channel, such as select and range, makes channel blocking more hidden and difficult to find, thus increasing the difficulty of troubleshooting memory leaks.
When writing goroutines and debugging memory leaks, focus on channel-related operations, especially the four types of patterns listed in the article: premature return of functions, timeout leaks, multi-terminal read and write leaks, and channel iteration misuse.
This article is transferred from: https://sund.site/posts/2023/goroutine-leak/
This site is only for collection, and the copyright belongs to the original author.