pprof 分析内存泄漏需重点观察 alloc_space 增量而非仅 inuse_space,通过差分分析定位持续分配不释放的函数;goroutine 泄漏要关注阻塞态及增长趋势;高频泄漏源为全局变量、Ticker/Timer 和未关闭资源。

用 pprof 抓 heap 快照,但别只看 inuse_space
内存泄漏最直观的信号是 inuse_space 持续缓慢上涨,但很多真实泄漏(比如全局 map 不断塞入、channel 未关闭导致 goroutine 持有对象)在 inuse_space 里占比极小,单次快照根本看不出异常。这时候必须做「差分分析」:间隔 2–5 分钟分别抓两个 heap profile,用 go tool pprof -diff_base heap1.pprof heap2.pprof 查看新增分配。重点关注 alloc_space 增量大的函数——哪怕它当前 inuse_space 很低,只要持续 alloc 却不释放,就是高危点。
- 访问
http://localhost:6060/debug/pprof/heap?gc=1可强制 GC 后采样,排除临时对象干扰 -
浏览器打开
web或svg时,右键点击函数 →list,能直接看到该函数中哪行代码分配了对象 - 避免在压测中途突然采样:先让服务稳定运行 1–2 分钟,再开始计时抓取,否则噪声太大
goroutine 数量不是“看一眼就完事”,要盯住阻塞态和增长趋势
runtime.NumGoroutine() 返回的是总数,但真正危险的是长期处于 chan receive、select 或 semacquire 的 goroutine。它们不消耗 CPU,却死死占着栈内存和引用的对象。
- 用
go tool pprof http://localhost:6060/debug/pprof/goroutine?debug=1查看文本堆栈,搜索chan receive或IO wait,定位卡在哪一行 - 对比两次快照:第一次服务空载时抓
goroutine,跑 10 分钟后再抓一次,pprof里用top看新增 goroutine 的调用栈,90% 的泄漏源头就藏在这里 - 别信“我用了 context.WithTimeout 就安全”——如果 channel 发送端没关,接收端即使带 timeout 也会在
case 后退出,但若漏掉default或误写成for range ch,照样泄漏
别忽略 GODEBUG 和 goleak 这两个“静默报警器”
pprof 是事后分析,而 GODEBUG 和 goleak 能在问题刚冒头时就拉响警报。尤其在测试阶段,它们比任何人工监控都可靠。
- 启动时加
GODEBUG=gctrace=1,观察 GC 日志里scvg(垃圾回收器收缩堆)是否频繁失败;若连续几次scvg都说not enough heap,说明有对象被意外持有 - 测试代码里引入
github.com/uber-go/goleak,在TestMain中调用goleak.VerifyNone(m),它会自动捕获测试结束后残留的 goroutine,并打印初始创建位置——比翻日志快十倍 - 线上环境慎用
GODEBUG=goprobe=1,它会显著增加调度开销;优先用 Prometheus 暴露runtime.NumGoroutine()指标,配 Grafana 告警阈值(如 5 分钟内增长 >200)
检查三类高频泄漏源:全局变量、Ticker/Timer、未关闭资源
80% 的泄漏集中在三个地方:全局 map/slice 无清理逻辑、time.Ticker 忘记 Stop()、文件/连接/通道未显式关闭。它们共同特点是“不报错、不崩溃、只悄悄吃内存”。
立即学习“go语言免费学习笔记(深入)”;
- 全局缓存务必设过期策略:
sync.Map不解决泄漏,cache = make(map[string]*Item)必须配套定时清理 goroutine 或使用github.com/bluele/gcache这类带 LRU 的库 -
time.Ticker必须配defer ticker.Stop();time.AfterFunc相对安全,但若传入的函数本身启动新 goroutine 且未控制生命周期,仍会泄漏 - 所有
os.Open、http.Client.Do、sql.DB.Query后,立刻跟defer xxx.Close();用lsof -p PID查看句柄数是否随时间线性增长,是判断资源泄漏最硬的指标
真正难的不是找到泄漏点,而是确认“它为什么没被 GC”——往往是一行看似无害的赋值,让某个大对象被一个长生命周期 goroutine 意外引用。所以每次看到可疑的 inuse_objects 增长,先查它的调用栈顶端是否连着全局变量或常驻 goroutine,而不是急着改业务逻辑。










