真泄漏必须同时满足heapinuse和heapalloc在稳定负载下线性增长、重启归零复现,且gc后heapinuse不回落;需避开gc频繁期采样,用pprof差分inuse_space定位净增长对象,并排除cgo、unsafe、goroutine泄漏等pprof不可见场景。

确认是不是真泄漏,别被 GC 滞后骗了
内存曲线往上走 ≠ 泄漏。真泄漏必须同时满足:稳定流量下 HeapInuse 和 HeapAlloc 随时间线性增长、每次重启归零、GC 后 HeapInuse 不回落甚至更高。如果刚启动时涨得猛、几分钟后就平稳,大概率是框架连接池预热或 GC 还没“热起来”,不是泄漏。
线上建议用 runtime.ReadMemStats 每 30 秒打点,画出 HeapInuse 曲线;容器 RSS 持续涨但 HeapInuse 平稳?那可能是 cgo 分配、unsafe 持有或 netpoll 表,pprof 看不见。
抓 heap profile 的三个硬坑,踩一个就白忙
线上抓快照,必须避开这三件事:
- 绝对不要加
?gc=1:它强制 GC 后采样,会把本该长期存活的泄漏对象“刷掉”,profile 里只剩临时垃圾 - 文件名必须带
.heap或.pb.gz后缀(如before.heap),否则go tool pprof报错unrecognized profile format - 两次采样间隔 ≥30 秒,且服务已运行超 5 分钟、流量稳定;刚启动或
MemStats.NextGC接近HeapAlloc时采样,快照全是待 sweep 的垃圾,数据失真
推荐做法:wget http://localhost:6060/debug/pprof/heap -O before.heap → 等 30 秒 → wget http://localhost:6060/debug/pprof/heap -O after.heap
Gin 场景下最常踩的泄漏点
Gin 本身不泄漏,问题出在你写的 handler 和中间件里:
-
http.HandlerFunc闭包捕获大结构体:比如在router.GET("/api", func(c *gin.Context) { data := loadBigData(); c.JSON(200, data) })里,data被闭包持有,随请求累积 - 中间件里滥用
context.WithValue存大对象,又没清理:值塞进去容易,但 Gin 不帮你删,全局 context 树越长越重 - 全局
sync.Map或map[string]*T往里塞东西从不删:比如用json.Unmarshal解析配置进 map,key 永远不淘汰 - 忘记
defer c.Request.Body.Close():虽然 Gin 通常帮你关了,但你自己用c.Request.Body做二次读取时漏掉,就会堆积io.ReadCloser
特别注意:Gin 的 c.Copy()、c.GetRawData() 返回的 []byte 如果赋给全局变量,底层数组会被钉住——不是 slice 本身,是它背后的 array。
pprof 差分定位时,Focus 别输错类型名
看到 *UserConfig 占内存多,不代表你在代码里写了 &UserConfig{}。它可能来自:
-
json.Unmarshal直接塞进全局map[string]*UserConfig -
sync.Map.LoadOrStore("id", cfg)后没设过期机制 - HTTP handler 闭包间接持有,调用栈只显示
gin.(*Context).Next,根本看不到你的业务逻辑
正确操作:go tool pprof -http=:8080 -base before.heap after.heap → 浏览器打开 → 右上角 SAMPLE 切到 inuse_space → View → Difference → Focus 输入 *UserConfig → Call graph 查谁在 new 它且没释放。重点盯调用链里夹着 timerproc、chan receive、(*ConnPool).reaper 的路径——这些才是钉子入口。
goroutine 泄漏常被当成内存泄漏,但它才是真正拖垮内存的元凶:每个卡死的 goroutine 默认占 2KB 栈,还可能钉住几 MB 的 buffer。务必用 curl "http://localhost:6060/debug/pprof/goroutine?debug=2" 查状态为 chan receive 或 select 的堆栈,尤其关注重复出现的行号(如 client.go:72)。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











