核心是“采样准、栈完整、符号全、干扰小”:需精准绑定pid轻量采样(-f 99、cpu-clock、--call-graph dwarf),确保二进制含-g调试信息及内核符号就位,用--all保留全栈,本地生成svg后通过/搜索和点击聚焦定位真实瓶颈。

直接对生产环境高负载进程做火焰图分析,核心是“采样准、栈完整、符号全、干扰小”。不能停服务、不能大幅拖慢业务,关键在控制采样开销和确保调用栈可读。
精准定位目标进程并轻量采样
避免全局采集(-a)带来的系统扰动,优先绑定具体 PID:
- 用 pgrep 或 pidof 快速获取进程 ID,例如:
pgrep -f "nginx: worker"或pidof java - 采样命令加 -F 99(非默认 1000 Hz),避开硬件中断对齐,降低抖动;用 cpu-clock 替代 cycles,更稳定反映真实 CPU 占用
- 单次采样建议 20–60 秒,命令示例:
sudo perf record -e cpu-clock -g --call-graph dwarf -F 99 -p 1234 -- sleep 30 - 若进程短时爆发(如毛刺型 CPU 尖峰),可临时提频至 -F 999,但需确认磁盘 I/O 和 perf.data 写入压力可控
确保调用栈不截断、函数名不显示 [unknown]
火焰图中大量 [unknown] 意味着符号解析失败,不是图没画好,而是数据缺关键信息:
- 用户二进制必须含调试信息:
file your_binary应显示 “with debug_info”;编译时务必加 -g,C/C++ 避免 strip,Java 需启用-XX:+PreserveFramePointer或--perf-basic-prof,Node.js 同理 - 内核符号要就位:
sudo cat /proc/kallsyms | head -3有输出才表示已加载;缺则安装对应linux-image-$(uname -r)-dbgsym(Ubuntu)或kernel-debuginfo(RHEL/CentOS) - 对多线程或内核路径敏感的问题,务必加 --all 参数:用
perf script | ./stackcollapse-perf.pl --all > out.folded,保留 sys_read→ext4 等关键内核路径,否则可能漏掉 I/O 瓶颈源头
本地生成火焰图并快速聚焦瓶颈
把 perf.data 拷回开发机处理,既安全又灵活:
- 用
scp user@prod:/path/perf.data ./拉取原始数据 - 执行三步转换:
perf script > perf.unfold→./stackcollapse-perf.pl --all perf.unfold > perf.folded→./flamegraph.pl perf.folded > flamegraph.svg - 打开 SVG 后,用浏览器 / 键搜索关键词(如 malloc、epoll_wait、json_encode),比肉眼扫图快得多;点击宽函数块可聚焦其子调用链
- 宽度代表 CPU 时间占比——不是“被调用次数”,而是“正在执行该函数的时间总和”,所以顶层叶子函数宽,说明它本身耗时长;底层宽,说明它被高频调用且每个调用不短
从火焰图反推优化动作
图只是线索,真正调优要结合代码和上下文判断:
- 若 vsnprintf 占比异常高,先查日志逻辑是否在过滤前就做了格式化(如未判 level 就调 vsnprintf)
- 若 std::list::size() 出现在热路径,注意它是 O(n),应换 vector 或缓存 size
- 若大量时间卡在 malloc / free,考虑内存池、对象复用或 jemalloc 替代
- 若火焰顶端频繁出现 __libc_malloc + 底层业务函数,大概率是高频小对象分配,而非单纯 GC 问题











