应检查pprof是否注册到默认mux:确保import _ "net/http/pprof"且http.ListenAndServe(":6060", nil),若用自定义mux或框架需手动挂载;访问/debug/pprof/返回404即未生效,curl验证是关键。

pprof 启动后看不到 heap 数据?检查 net/http/pprof 是否真生效
启动了 http.ListenAndServe 并导入 _ "net/http/pprof",但访问 /debug/pprof/heap 返回 404 或空页面——大概率是 pprof 路由没挂到默认 mux 上。
常见错误包括:
- 手动创建了
http.ServeMux但没传给ListenAndServe,导致 pprof 的 handler 没注册进去 - 用的是第三方 HTTP 框架(如 gin、echo),没显式把 pprof 路由注册到对应 router
- 服务监听地址写成
"localhost:6060",但本地浏览器访问的是127.0.0.1:6060(某些系统下 hostname 解析失败)
验证方式:curl -v http://localhost:6060/debug/pprof/ —— 应返回 HTML 页面;若返回 404,说明 pprof 根路径未暴露。最简修复就是直接用 nil 作为 handler:http.ListenAndServe(":6060", nil),让标准库自动注入 pprof 路由。
go tool pprof 连不上远程服务?确认端口、超时与 GC 状态
执行 go tool pprof http://localhost:6060/debug/pprof/heap 卡住或报错 Get "http://...": context deadline exceeded,不是网络问题,而是服务端没在分配内存或 GC 尚未触发。
立即学习“go语言免费学习笔记(深入)”;
关键点:
- pprof heap profile 默认只记录「当前存活」对象,如果程序刚启动、还没分配大对象,
inuse_space可能接近 0,go tool pprof会等几秒后放弃 - Go 1.21+ 默认
GOGC=100,意味着堆增长 100% 才触发 GC;若测试逻辑短、分配少,GC 不发生,heap 快照就几乎为空 - 加
-seconds=30参数强制采集 30 秒内所有分配:go tool pprof -seconds=30 http://localhost:6060/debug/pprof/heap
更可靠的做法:先让程序持续运行并产生内存压力(比如开个 goroutine 循环 make([]byte, 1),再采集。
测试中用 -memprofile 却没生成文件?时机和速率必须匹配
go test -memprofile=mem.out 运行完发现目录下没有 mem.out,不是命令写错,而是测试提前 OOM 崩溃了——-memprofile 只在测试进程正常退出时才写入文件。
真正有效的组合是:
-
-memprofilerate=1:每次分配都记录(仅调试用,性能损耗极大) -
-gcflags="-l":禁用内联,让函数调用栈更清晰 - 配合
runtime.GC()强制在关键点触发回收,避免“假泄漏”干扰判断
示例命令:go test -memprofile=mem.out -memprofilerate=1 -gcflags="-l" ./...。注意:上线前务必把 -memprofilerate 改回默认值(或设为 1024*1024),否则压测时 CPU 全耗在采样上了。
为什么 runtime.ReadMemStats 显示 Alloc 持续上涨,但 pprof 里找不到大户?
这是最易误判的点:runtime.ReadMemStats().Alloc 是「已分配且尚未被 GC 回收」的字节数,但它不等于「泄漏」。GC 可能只是延迟了,尤其当堆还远低于触发阈值时。
真正要盯的指标是:
-
HeapSys - HeapReleased:实际向 OS 申请的内存,持续上涨才可疑 -
NumGC长时间不变:说明 GC 几乎没跑,得查GOGC设置或是否禁用了 GC - pprof 中看
inuse_space(而非alloc_space):前者才是当前存活对象占用
一个典型陷阱:用 sync.Pool 复用对象后,inuse_space 看似稳定,但 HeapSys 持续涨——Pool 里的对象被 GC 清理后,内存未必立刻归还 OS,需等 runtime/debug.FreeOSMemory()(慎用,影响性能)。


















