CPU占用高90%非业务逻辑慢,而是goroutine泄漏、锁争用、channel阻塞或频繁系统调用;pprof能定位,但需采样≥30秒、带符号表二进制、避开runtime假热点,否则显示runtime.futex或地址乱码。

CPU 占用高,90% 不是业务逻辑“算得慢”,而是 goroutine 泄漏、锁争用、channel 阻塞或频繁系统调用;pprof 能定位,但必须采样够久、带符号表二进制、避开 runtime 假热点,否则看到的全是 runtime.futex 或地址乱码。
curl 下载 cpu.pprof 时为什么总拿到 HTML 页面?
浏览器直接访问 http://localhost:6060/debug/pprof/profile 返回的是交互式 HTML 页面,不是二进制 profile 文件——这是最常踩的坑。
- 必须用
wget或curl -s显式下载:wget "http://localhost:6060/debug/pprof/profile?seconds=30" -O cpu.pprof - 容器内执行:
kubectl exec -it <pod> -- curl -s "http://localhost:6060/debug/pprof/profile?seconds=30" > cpu.pprof</pod> -
?seconds=5容易错过间歇性热点;?seconds=300又会混入空闲期噪声,推荐 20–40 秒区间 - 验证是否成功:运行
go tool pprof -top cpu.pprof,第一行应显示类似Showing nodes accounting for 28.41s, 99.97% of 28.42s total,且能列出具体函数名(如encoding/json.(*decodeState).object)
go tool pprof ./myapp cpu.pprof 里全是 [unknown] 或 0x 地址?
因为 pprof 需要可执行文件里的 DWARF 符号表做地址映射,只传 cpu.pprof 就等于给它一张没坐标的地图。
- 必须同时提供二进制:
go tool pprof ./myapp cpu.pprof(./myapp是你用go build生成的,默认带符号) - 禁用
-ldflags="-s -w":这个参数会剥离符号表,发布版常用,但分析阶段绝对不能用 - 验证符号是否存在:
readelf -S ./myapp | grep debug有输出,或file ./myapp显示with debug_info - 已
strip的二进制无法补救,只能重编译;CI/CD 中建议 dev/test 环境保留符号,prod 留一份带符号 debug 版归档备用
火焰图里 runtime.futex 占比超高,是不是要优化锁?
不一定。高占比的 runtime.futex、runtime.netpoll 或 syscall.Syscall 通常是表象,背后大概率是 goroutine 泄漏或阻塞资源等待。
立即学习“go语言免费学习笔记(深入)”;
- 先查
goroutine数量趋势:curl "http://localhost:6060/debug/pprof/goroutine?debug=2" > goroutines.txt,搜索chan receive、semacquire、selectgo,看是否有重复堆栈大量堆积 - 对比
debug=1和debug=2输出,观察goroutine总数是否持续增长(如 5 分钟内从 2k 涨到 15k) - 典型诱因:未缓冲
channel单侧写入、time.After在循环中未释放、数据库连接池耗尽后阻塞在semacquire
真正难处理的不是火焰图上哪个函数红得刺眼,而是那些不显眼却持续增长的 goroutine 堆栈,它们不会在单次 CPU 采样里暴露,但会让服务在几小时后悄悄卡死。记得每次拉 cpu.pprof 前,先看一眼 /debug/pprof/goroutine?debug=1 的总数趋势。


















