
比如常见的异步生产消费模型,下面这段代码你能快速定位泄漏问题吗?如果你不停的访问这个 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
1 Nxxx 2022 年 4 月 1 日 最近刚好遇到这个问题 回去是下 以 star |
2 zagfai 2022 年 4 月 2 日 严格来说又不算 bug 算 feature 吧?。。。 |
3 ysmood OP |