火焰图中main下最宽函数即CPU热点,但需确保开启采样、保留帧指针且采集时间足够;Go生产构建须加-gcflags="-l -N"保留符号与帧指针,并启用net/http/pprof。

直接看火焰图,main底下最宽的那条函数就是当前CPU热点——但前提是采样开了、帧指针没被优化掉、采集时间够长。
怎么生成能看懂的火焰图
火焰图不是“有图就行”,它依赖底层栈帧可回溯。Go 默认编译会去掉帧指针(-fno-omit-frame-pointer),导致采样到一堆 0x41a2c8 这样的地址,根本没法定位函数。
- 生产构建务必加
go build -gcflags="-l -N" -ldflags="-s -w" -buildmode=exe,其中-N禁用内联,-l禁用优化,确保符号和帧指针完整 - pprof HTTP 服务必须启动:导入
_ "net/http/pprof"并运行http.ListenAndServe(":6060", nil) - 采集命令要带足够时长:
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30,少于15秒容易漏掉偶发但高频的调用 - 生成火焰图需先装
graphviz(macOS:brew install graphviz;Ubuntu:apt install graphviz)
火焰图里怎么看“谁在烧CPU”
横轴是CPU时间占比(不是真实时间),纵轴是调用栈深度。越宽的矩形,代表该函数及其子调用消耗的CPU越多;顶部宽条才是真热点,别被中间某层“看起来高”的假象骗了。
- 重点盯住从
main或http.HandlerFunc开始一路向下、持续变宽的路径 - 如果看到
regexp.(*Regexp).MatchString占满半张图,大概率是路由或日志里滥用正则;换成前缀判断或预编译复用 - 若
encoding/json.Marshal突然变宽,检查是否在循环里反复序列化同一结构体,考虑缓存结果或改用json.RawMessage - 注意“扁平宽条”:比如
runtime.mallocgc占比高,说明GC压力大,根源可能在热点函数里频繁 new 对象
常见误操作导致火焰图失效
不是图没生成,而是图里全是问号或地址,等于白忙活。
立即学习“go语言免费学习笔记(深入)”;
- 用
go run main.go直接跑——不行。必须go build后运行二进制,否则符号表缺失 - 在 Docker 容器里跑但没暴露
6060端口,或宿主机连不上容器内网,采集返回空数据 - 采集时程序负载太低,30秒内没触发目标逻辑,火焰图一片空白或只有 runtime 函数
- 用
web命令生成调用图(非火焰图),误以为那是火焰图——web是调用树,-http=:8080才是火焰图
真正卡住人的从来不是“怎么画图”,而是图里看不到函数名。宁可多花两秒加 -N -l 重编译,也别对着一屏十六进制地址猜半天。线上服务尤其要提前验证 pprof 接口返回的 profile 是否含有效 symbol。









