pprof 抓不到 CPU 火焰图需先确认采样是否发生,检查是否误设 GODEBUG 调试开关、容器内 perf_event_paranoid 值过高,或尝试提高采样频率至 1kHz;火焰图中 runtime.futex/runtime.netpoll 高频出现应查 goroutine 泄漏与 channel 阻塞;list 命令可精确定位函数内耗时行;线上采集应短时(5–10 秒)下载后本地分析。

pprof 抓不到 CPU 火焰图?检查 runtime.SetBlockProfileRate 和采样时长
默认情况下,pprof 的 CPU profile 依赖操作系统信号(SIGPROF)定时中断,每秒约 100 次(即 10ms 间隔)。但若程序在大量系统调用、阻塞 I/O 或 runtime 自旋中停留过久,实际采样点可能稀疏甚至空白——表现为火焰图扁平、函数堆栈缺失、top 显示高 CPU 却 profile 里全是 runtime.mcall 或空帧。
关键不是加长采集时间,而是确认是否真有采样发生:
- 用
curl 'http://localhost:6060/debug/pprof/profile?seconds=30'显式指定 30 秒,避免默认 30 秒超时被提前终止 - 若仍无有效数据,立即检查是否误设了
runtime.SetBlockProfileRate(0)(这会禁用 block profile,但不影响 CPU profile);真正影响 CPU 采样的,是GODEBUG=schedtrace=1000这类调试开关的干扰,或容器内perf_event_paranoid值过高(需设为0或1) - 极端情况可临时启用更细粒度采样:
curl 'http://localhost:6060/debug/pprof/profile?seconds=30&hz=1000'(将采样频率提到 1kHz),但仅限短时诊断,长期开启会引入可观开销
火焰图里全是 runtime.futex 和 runtime.netpoll?重点查 goroutine 泄漏和 channel 阻塞
这类符号高频出现,通常不表示内核问题,而是 Go runtime 在等待底层资源:goroutine 被挂起在 channel send/recv、锁竞争、或网络连接未关闭。此时 go tool pprof -http=:8080 cpu.pprof 打开的火焰图只是表象,真正线索藏在 goroutine profile 中。
立刻执行:
-
curl 'http://localhost:6060/debug/pprof/goroutine?debug=2' > goroutines.txt,搜索chan receive、semacquire、selectgo等状态,统计重复堆栈数量 - 对比
curl 'http://localhost:6060/debug/pprof/goroutine?debug=1'的简略版,看 goroutine 总数是否持续增长(如 5 分钟内从 2k 涨到 15k) - 检查代码中是否有未缓冲的 channel 被单侧使用(如只 send 不 recv)、
time.After在 for 循环里反复创建、或http.Client未设置Timeout导致连接堆积
定位到具体函数后,go tool pprof 的 list 命令比火焰图更准
火焰图擅长展示调用热点分布,但无法精确到某一行耗时——尤其当函数内含多个分支、条件判断或内联代码时。比如火焰图显示 processOrder 占 45% CPU,但你不确定是序列化慢、还是数据库查询慢、或是循环里反复调用 fmt.Sprintf。
这时直接进交互式分析:
-
go tool pprof cpu.pprof后输入list processOrder,它会显示该函数汇编指令级的采样计数,每一行标注命中次数 - 注意区分
CALL指令后的函数名:若某行CALL runtime.convT2E占比异常高,说明存在高频接口类型断言;若CALL runtime.gcWriteBarrier突出,则可能是小对象分配过于密集 - 配合源码行号交叉验证:确保编译二进制启用了调试信息(未加
-ldflags '-s -w'),否则list输出只有汇编无源码
线上环境不敢停服务?用 net/http/pprof 的增量采样 + 本地复现验证
生产环境不允许长时间挂起或重启进程,但 /debug/pprof 是运行时 HTTP handler,本身开销极低(CPU profile 采样本身由内核信号驱动,HTTP 请求只负责拉取已采集数据)。风险点不在采集,而在误操作导致 profile 文件过大或下载超时。
安全做法:
- 先用
curl -o mem.pprof 'http://prod-host:6060/debug/pprof/heap'快速抓内存快照(heapprofile 不依赖采样,瞬时生成) - CPU profile 限定 5–10 秒:
timeout 15s curl -o cpu.pprof 'http://prod-host:6060/debug/pprof/profile?seconds=5',避免因网络卡顿导致请求悬停 - 拿到 profile 后,**不要直接在线上分析**:用
go tool pprof -http=:8080 cpu.pprof在本地打开,所有计算在你机器完成;火焰图中点击任意函数,右键 “Open in editor” 可跳转到本地对应源码行(需路径匹配)
真实瓶颈往往藏在看似无害的细节里:一个没加 context.WithTimeout 的下游 HTTP 调用,在连接池耗尽后会让整个 goroutine 阻塞在 runtime.netpoll;或者一段本该用 strings.Builder 拼接字符串的逻辑,因为写成了多次 += 触发频繁内存分配和 GC 压力。这些都不会在日志里报错,但会在 pprof 数据里留下清晰痕迹。


















