📌 前言
使用 async-profiler 生成 CPU 火焰图时,我们通常认为:
🔥"宽度越宽的函数,占用 CPU 越多。"
但很多工程师不知道:
火焰图的宽度并不是绝对 CPU 时间,而是基于采样概率统计出的热点分布。
这篇文章将系统性解释:
-
火焰图宽度为什么大多数时候可靠
-
在哪些场景下会"误导"
-
如何避免误判
-
给出更专业的 Profiling 使用建议
目录
-
一、火焰图宽度到底表示什么?
-
二、哪些情况会导致火焰图"误导"?
-
三、async-profiler 的两个典型误区
-
四、如何检查火焰图是否准确?
-
五、如何获得更准确的火焰图?
-
六、总结:火焰图宽度可靠但非绝对
一、火焰图宽度到底表示什么?
async-profiler 使用 异步采样(async sampling) 技术进行 CPU 分析,每隔固定时间(例如 1ms)采集一次当前正在运行的栈帧。
因此:
✔ 宽度 = 采样次数
即某段代码被采到的次数越多,它的"火焰图宽度"就越宽。
✔ 采样次数 ≈ CPU 时间
采样频率足够高时,统计结果会非常接近真实的 CPU 耗时分布。
二、哪些情况会导致火焰图"误导"?
虽然 async-profiler 的技术十分先进,但火焰图仍可能出现偏差,典型情况如下:
(1)函数执行过短,采样概率太低
例如:
-
JIT inline 后的极短小函数
-
JVM intrinsic(如
System.arraycopy优化) -
Hotspot 编译的 tight loop
这些函数可能执行了数百万次,但由于采样概率太低 → 火焰图中几乎看不到。
表现:CPU 明明很高,但图上没有对应热点。
(2)线程处于阻塞状态(锁、IO、park)
CPU firegraph 只统计"线程正在跑 CPU"时的栈。
例如:
-
synchronized 竞争
-
ReentrantLock.await
-
IO 等待
-
sleep / park
这些都会让代码 不出现在火焰图中。
会误导你以为:
"CPU 很低,没有热点问题",
实际上是 锁竞争 导致的吞吐下降。
(3)JIT 内联导致热点函数消失
Java 热点函数可能被 JIT inline 到上层函数中。
结果:
-
被 inline 的小函数不会出现在图里
-
宽度被算到上层函数头上
-
人类阅读时可能误判是"外层函数耗 CPU"
(4)Native / Kernel 调用栈不完整
当涉及以下函数时:
-
unsafe.copyMemory
-
JNI 调用
-
系统调用(read, write, epoll_wait)
-
malloc/free
-
GC safepoint
async-profiler 可能无法 100% 解析 native 栈帧,会出现:
-
unknown -
[kernel] -
[native]
如果你误读这些,就可能找到错误的热点方向。
(5)容器环境 CPU Throttle(K8s)
例如 cgroup 限制 CPU:
-
Java 线程其实被限制了,没有运行
-
火焰图宽度变窄
-
但真实 CPU 还是不足(被 throttle)
表现:
图上看似 CPU 很低,但 QPS 明显下降。
(6)采样频率过低导致 aliasing 问题
例如采样周期为 1ms,但代码执行时间模式与采样周期发生周期性重叠,会导致统计偏差。
三、async-profiler 的两个典型误区
(1)把 Wall-clock 火焰图当 CPU 火焰图
-e wall
统计的是 线程总时间(运行 + 等待),不是 CPU 时间。
很多人用错模式导致完全错误的结论。
(2)没有开启真实异步栈采集
必须使用:
-e cpu
async-profiler 才能完全避免 safepoint-only 的问题。
四、如何检查火焰图是否准确?
你可以通过以下方式交叉验证:
✔(1)查看 perf top / perf record
如果 perf 显示大量 syscall、kernel 执行,而火焰图没有 → 表示丢帧。
✔(2)看 collapse file 中 unknown 数量
./profiler.sh -e cpu -f out.collapsed <pid>
unknown 比例高说明栈不完整。
✔(3)看 JIT 日志
-XX:+UnlockDiagnosticVMOptions -XX:+PrintCompilation
如果热点函数被 inline,火焰图一定会被"重写"。
五、如何获得更准确的火焰图?
推荐配置:
./profiler.sh \ -e cpu \ -i 1ms \ -j \ --cstack kernel \ -f cpu-flame.html \ <pid>
参数说明:
| 参数 | 说明 |
|---|---|
-e cpu |
真正的 CPU Profiling |
-i 1ms |
更高采样频率 |
-j |
展示 JIT 行内栈 |
--cstack kernel |
捕获 native + kernel 栈 |
六、总结:火焰图宽度可靠但非绝对
✔ 可靠的地方
火焰图宽度可以 稳定地标识 CPU Hotspot,适合定位:
-
CPU 密集代码
-
算法热点
-
自旋锁
-
高频调用逻辑
❗不可靠的地方
它不能精准表达:
-
极短函数耗时
-
锁竞争
-
IO 阻塞
-
内联消失的函数
-
容器 throttle 导致 CPU 不足
📌 最终一句话总结
async-profiler 的火焰图宽度非常有价值、非常可靠,但它描述的是概率意义上的 CPU 热点,不是绝对精确的 CPU 时间,因此在特定场景下可能出现误导,需要结合线程状态、JIT、perf 等多维度一起分析。