检测 goroutine 泄漏

2022-04-01 17:21:48 +08:00
 ysmood

比如常见的异步生产消费模型,下面这段代码你能快速定位泄漏问题吗?如果你不停的访问这个 3000 端口,网站会内存泄漏。

package main

import (
	"fmt"
	"net/http"
)

func main() {
	_ = http.ListenAndServe(":3000", http.HandlerFunc(handle))
}

func handle(_ http.ResponseWriter, _ *http.Request) {
	c := make(chan int)
	go produce(c)
	go consume(c)
}

func produce(c chan int) {
	for i := range "..." {
		c <- i
	}
}

func consume(c chan int) {
	for i := range c {
		fmt.Println(i)
	}
}

我们可以用 gotrace 来自动定位泄漏点,添加一个 main_test.go 文件:

package main

import (
	"testing"

	"github.com/ysmood/gotrace"
)

func TestHandle(t *testing.T) {
	gotrace.CheckLeak(t, 0)

	handle(nil, nil)
}

运行 GODEBUG="tracebackancestors=10" go test,等待 3 秒后就可以看到打印信息如下:

--- FAIL: TestHandle (3.00s)
    main_test.go:10: leaking goroutines: goroutine 6 [chan receive]:
        play.consume(0x0)
        	/Users/ys/repos/play/default/main.go:25 +0x74

可以看到第 25 行的 range 卡住了,原因是 chan receive,它在等待 c 的新消息,然而 produce 函数已经退出不会再往 c 里发布消息了。

修正的方法很多,比如把 produce 修改为:

func produce(c chan int) {
	for i := range "..." {
		c <- i
	}
	close(c)
}

再运行 go test 测试就立马成功结束了。

想了解更多 gotrace 用法可以移步 https://github.com/ysmood/gotrace

1473 次点击
所在节点    分享发现
3 条回复
Nxxx
2022-04-01 23:48:45 +08:00
最近刚好遇到这个问题 回去是下 以 star
zagfai
2022-04-02 00:53:48 +08:00
严格来说又不算 bug 算 feature 吧?。。。
ysmood
2022-04-02 01:19:20 +08:00
@zagfai 什么意思?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/844363

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX