内存泄漏的常见场景
- goroutine 泄漏:goroutine 创建后阻塞,不会退出。(解法:context超时控制)
- channel 中有数据堆积(无人消费)
- slice 长时间持有底层数组的引用。
goroutine 泄漏
package main
import (
"fmt"
"runtime"
"time"
)
func handleRequest(id int, respCh <-chan string) {
go func() {
_ = <-respCh
}()
}
func main() {
respCh := make(chan string)
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
for i := 0; i < 1000; i++ {
handleRequest(i, respCh)
if i%100 == 0 {
fmt.Printf("after spawning %d goroutines, runtime.NumGoroutine() = %d\n",
i, runtime.NumGoroutine())
}
}
for j := 0; j < 10; j++ {
<-ticker.C
fmt.Printf("tick %d: NumGoroutine = %d\n", j, runtime.NumGoroutine())
}
fmt.Println("done")
}
排查内存泄漏的思路
- 现象:CI阶段uber/go-leak发现了内存泄漏(比较协程池数量),或者内存随着时间升高。
- 开启
GODEBUG=gctrace=1,观察 GC 日志:如果 GC 频繁运行但内存占用依旧增长 → 高概率是内存泄漏。
- 通过pprof检查是对象没回收 还是 goroutine 没关闭。
- 通过pprof查看内存分配情况:pprof 中 top 和 火焰图 都有。
- 定位代码,解决问题。
排查内存泄漏的工具
go tool pprof http:
curl http: