jfr 命令配合 jdk.executionsample 事件可精准捕获毫秒级 cpu 热点,因其默认每 10ms 采样线程栈,远超 top 和 jstack 的粗粒度与单次快照局限,是生产环境瞬时 cpu 爆发的唯一可靠手段。

直接用 jfr 命令配合 jdk.ExecutionSample 事件就能定位热点方法,不需要 JDK Mission Control 或图形界面。
为什么不能只看 top 或 jstack
top 显示的是整体 CPU 使用率,看不出哪个线程、哪段代码在抖动;jstack 是单次快照,200ms 的尖峰大概率被漏掉。而 jdk.ExecutionSample 默认每 10ms 采样一次线程栈,能稳定捕获毫秒级热点——这才是生产环境里真正能抓到“瞬时 CPU 爆发”的唯一手段。
常见错误现象:
– 用 jstack 抓了 5 次,全是 WAITING 状态,但 CPU 却持续 80%+
– top -H 看到某个线程 PID 占用高,却无法关联到具体方法
– 开了 APM 工具,但采样间隔是 1s,抖动发生在 300ms 内,数据全被平均掉了
用 jcmd 动态启动高频 CPU 录制(不重启服务)
先查 Java 进程 ID:jcmd -l
再启动 30 秒高精度录制(含 JIT 和线程事件):jcmd <pid> JFR.start name=cpu-burst settings=profile duration=30s filename=/tmp/cpu-burst.jfr maxsize=50M</pid>
关键点:
– 必须加 settings=profile,否则默认配置不启用 jdk.ExecutionSample
– maxsize=50M 是硬性要求,否则环形缓冲区溢出,会丢掉抖动前的关键 JIT 逆优化事件
– 如果抖动有规律(比如每 5 分钟一次),可用 delay=4m30s 避免提前录制浪费空间
命令行快速提取并统计热点方法
导出抖动窗口内的执行样本:jfr print --events jdk.ExecutionSample --begin "2026-10-13T17:02:10" --end "2026-10-13T17:02:12" /tmp/cpu-burst.jfr > samples.txt
提取并排序最频繁的栈顶方法:grep "stackTrace" samples.txt | sed 's/.*method=\"\([^"]*\)\".*/\1/' | sort | uniq -c | sort -nr | head -10
重点关注这几类方法名:
– com.fasterxml.jackson.databind.(JSON 序列化爆炸)
– java.util.regex.Pattern.(正则回溯)
– xxxService.process()(业务逻辑中未优化的循环或递归)
– sun.nio.ch.EPollArrayWrapper.epollWait(Netty native 层卡住,此时 jdk.ExecutionSample 不会显示 Java 栈,需换 async-profiler)
容易被忽略的两个事实
第一:jdk.ExecutionSample 默认不记录 native 方法栈。如果抖动来自数据库驱动的 JNI 调用、Netty 的 epoll_wait 或 glibc malloc,你看到的会是“空栈”或“Unknown”,不是工具失效,而是它本来就不覆盖这一层。
第二:JIT 逆优化往往比热点方法本身更关键。如果 jdk.JITUncompilation 事件在 CPU 抖动开始前 1–2 秒密集出现,说明 C2 编译器刚把某段代码退优化回解释执行——这时候看 jdk.ExecutionSample 的热点,只是结果,不是根因。











