
pprof 是 Go 自带的,不用额外安装
Go 1.0 起就内置了 net/http/pprof 和命令行工具 go tool pprof,只要用的是标准 Go 安装包(非精简版或某些嵌入式交叉编译环境),就直接可用。常见误区是去 go get 第三方 pprof 包——这不仅多余,还可能引入版本冲突或误用旧版接口。
验证方式很简单:go tool pprof -h 能打印帮助即说明就绪;go list -f '{{.Dir}}' runtime/pprof 能输出路径也说明标准库完整。
- Windows 用户注意:PowerShell 中执行
go tool pprof时若报“找不到命令”,大概率是GOBIN未加入 PATH,而非 pprof 缺失 - Alpine Linux 或极简容器镜像(如
golang:alpine)可能不含go tool pprof,需改用golang:slim或手动复制二进制 - Go 1.21+ 默认启用
runtime/trace的轻量采样,但 pprof HTTP 端点仍需显式注册,不会自动开启
HTTP 服务必须手动挂载 /debug/pprof 路由
pprof 不会自动监听或暴露任何端口。你得在 HTTP server 里显式导入并注册路由,否则访问 /debug/pprof 会返回 404。最简做法是导入 net/http/pprof 包(它有副作用,会自动注册 handler):
import _ "net/http/pprof"
但这只对默认 mux(http.DefaultServeMux)生效。如果你用了 http.NewServeMux()、Gin、Echo 或其他框架,必须手动挂载:
立即学习“go语言免费学习笔记(深入)”;
router := http.NewServeMux()
router.HandleFunc("/debug/pprof/", http.HandlerFunc(pprof.Index))
router.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
router.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))- 路径末尾的
/很关键:/debug/pprof/和/debug/pprof是两个不同路由,漏掉斜杠会导致子页面(如/debug/pprof/goroutine?debug=1)404 - Gin 用户别写
gin.Default().GET("/debug/pprof/*pprof", ...)—— Gin 的通配符不兼容 pprof 内部重定向逻辑,应改用gin.WrapH(http.DefaultServeMux) - 生产环境务必限制访问来源(如用中间件校验 IP 或加 Basic Auth),
/debug/pprof泄露 goroutine stack、heap 分布甚至符号表,风险极高
采集 CPU profile 要注意持续时间与信号权限
CPU profile 依赖操作系统信号(SIGPROF)定时中断,不是“快照”,而是采样统计。直接执行 curl 'http://localhost:8080/debug/pprof/profile?seconds=5' 可能返回空或报错,原因通常是:
- 目标进程没收到足够信号:Linux 上需确保进程未被
prctl(PR_SET_NO_NEW_PRIVS, 1)锁定,容器中要检查securityContext.capabilities.add是否包含SYS_PROFIL(K8s 1.25+ 已弃用,改用seccomp白名单) - 采样时间太短:低于 1 秒的
seconds参数常被忽略,建议至少设为 3–5 秒;若服务 QPS 极低,可临时提高负载再采 - Go 程序未启用 CGO:某些 musl libc 环境下(如 Alpine),CGO_ENABLED=0 会导致
runtime/pprof无法正确安装信号处理器,CPU profile 始终为空
验证是否成功:下载的 profile 文件用 go tool pprof -http=:8081 cpu.pprof 启服务后,看 Web 页面左上角是否显示 “Duration: Xs” 且火焰图有调用堆栈。
分析 heap profile 时别直接看 allocs,优先看 inuse_space
/debug/pprof/heap 默认返回的是 inuse_space(当前存活对象占用内存),但很多人误用 ?allocs 参数查“分配总量”。后者只反映累计分配字节数,不体现内存泄漏——即使所有对象都被 GC 回收了,allocs 也会飙升。
- 判断内存是否持续增长,看
inuse_space随时间的变化趋势,而不是某次 dump 的绝对值 - 用
go tool pprof -http=:8081 http://localhost:8080/debug/pprof/heap后,在 Web 界面右上角下拉菜单切换 “Sample” 类型,选inuse_space才有意义 - 如果
inuse_space稳定但 RSS 远高于它,可能是runtime.MemStats.Sys中的HeapSys - HeapInuse差值大,说明有大量释放后未归还给操作系统的内存(常见于频繁申请大块内存后释放)
真正难定位的是那些生命周期长、被意外持引用的对象——pprof 只能告诉你“谁分配了”,不能直接告诉你“谁没释放”,得结合代码里 pprof.Lookup("goroutine").WriteTo 查活跃 goroutine 栈,或者用 go tool trace 看 GC 暂停和对象晋升行为。











