|
2 | 2 |
|
3 | 3 | ### Goroutine leak profiles {#goroutineleak-profiles} |
4 | 4 |
|
5 | | -We introduce a new profile type for goroutine leaks. With the experimental |
6 | | -flag set to `GOEXPERIMENT=goroutineleakprofile`, it becomes accessible |
7 | | -through `pprof` under the name `"goroutineleak"`. |
| 5 | +A new profile type that reports leaked goroutines is now available as an |
| 6 | +experiment. The new profile type, named `goroutineleak` in the [runtime/pprof] |
| 7 | +package, may be enabled by setting `GOEXPERIMENT=goroutineleakprofile` |
| 8 | +at build time. Enabling the experiment also makes the profile available |
| 9 | +as a [net/http/pprof] endpoint, `debug/pprof/goroutineleak`. |
8 | 10 |
|
9 | | -The following snippet showcases a common, but erroneous pattern |
10 | | -that leads to goroutine leaks: |
| 11 | +The following example showcases a real-world goroutine leak that |
| 12 | +can be revealed by the new profile: |
11 | 13 | ```go |
12 | 14 | type result struct{ |
13 | 15 | res workResult |
@@ -37,27 +39,27 @@ func processWorkItems(ws []workItem) ([]workResult, error) { |
37 | 39 | return results, nil |
38 | 40 | } |
39 | 41 | ``` |
40 | | -Because `ch` is unbuffered, if `processWorkItems` returns early due to an error, |
41 | | -all remaining work item goroutines will leak. |
42 | | -However, also note that, soon after the leak occurs, `ch` is inaccessible |
43 | | -to any other goroutine, except those involved in the leak. |
44 | | - |
45 | | -To generalize, a goroutine is leaked if it is blocked by concurrency |
46 | | -primitives (specifically channels, and `sync` primitives such as mutex) that |
47 | | -are only referenced by the blocked goroutine itself, or other leaked goroutines. |
48 | | -The Go runtime is now equipped to reveal leaked goroutines by recording their stacks in |
49 | | -goroutine leak profiles. |
50 | | -In the example above, the stacks of work item goroutines point to the culprit channel send |
51 | | -operation. |
52 | | - |
53 | | -Note that, while goroutine leak profiles only include true positives, goroutine leaks may be |
54 | | -missed when caused by concurrency primitives that are accessible globally, or referenced |
55 | | -by runnable goroutines. |
| 42 | +Because `ch` is unbuffered, if `processWorkItems` returns early due to |
| 43 | +an error, all remaining `processWorkItem` goroutines will leak. |
| 44 | +However, `ch` also becomes inaccessible to all other goroutines |
| 45 | +not involved in the leak soon after the leak itself occurs. |
| 46 | + |
| 47 | +In general, a goroutine is leaked if it is blocked on an operation |
| 48 | +over concurrency primitives (e.g., channels, |
| 49 | +[sync.Mutex]) that are only reachable via leaked goroutines. |
| 50 | +In the example above, the culprit channel send operation involves |
| 51 | +a channel only reachable to `processWorkItem` goroutines. |
| 52 | +The runtime is now equipped to detect such leaks via goroutine |
| 53 | +leak profiles. All leaks reported by profiles are real, i.e., |
| 54 | +there are no false positives. |
| 55 | + |
| 56 | +Note, however, that the runtime may fail to identify leaks caused by |
| 57 | +blocking on operations over concurrency primitives reachable |
| 58 | +through global variables or the local variables of runnable goroutines. |
56 | 59 |
|
57 | 60 | Special thanks to Vlad Saioc at Uber for contributing this work. |
58 | 61 | The underlying theory is presented in detail by Saioc et al. in [this publication](https://dl.acm.org/doi/pdf/10.1145/3676641.3715990). |
59 | 62 |
|
60 | | -<!-- More details about the implementation are presented in the [design document](https://github.com/golang/proposal/blob/master/design/74609-goroutine-leak-detection-gc.md). --> |
61 | 63 | We encourage users to try out the new feature with examples derived from known patterns in [the Go playground](https://go.dev/play/p/3C71z4Dpav-?v=gotip), |
62 | 64 | as well as experiment with different environments (tests, CI, production). |
63 | 65 | We welcome feedback on the [proposal issue](https://github.com/golang/go/issues/74609). |
0 commit comments