Linux Java 线程 CPU 耗时怎么定位:JFR CPUTimeSample、throttle 与丢样复查
来源:17golang原创
时间:2026-08-21 07:52:03 142浏览 收藏
线上Java服务的接口耗时突然上升时,单看墙上时钟时间很容易误判:一个方法可能只是长时间等网络返回,另一个方法虽然单次请求很快,却在序列化、压缩或者字符串处理上吃掉了大量CPU。Java 25的JFR CPU-Time Profiling在Linux上按CPU实际消耗时间采样线程,刚好能把这两类问题明确区分开。
- Java 25新增的
jdk.CPUTimeSample是实验性JFR事件,只在Linux平台提供CPU时间采样能力。 - 用
jcmd采集运行中的JVM进程,用jfr view cpu-time-hot-methods就能完成第一轮文本层面的热点定位。 throttle=20ms更容易捕捉到短时运行的热点方法,但采样越密集,带来的性能开销和生成的数据体量也会越高。- 排查的时候要同时对照CPU热点和实际请求耗时,不能直接把CPU消耗时间等同于端到端接口延迟。
先把“慢”拆成CPU忙还是在等待
假设你维护的服务里有两个方法:tenFastRequests() 连续处理十次小请求,oneSlowRequest() 只处理一次逻辑但中间等待100ms。两者的总墙上耗时看起来差不多,但CPU实际消耗完全不一样。前者高并发下很容易成为CPU瓶颈,后者大概率只是在等socket返回数据。
传统的JFR执行采样主要按固定的时间间隔取样,线程处在等待状态的时候基本不会生成执行样本。CPU-time profiling依托Linux的CPU timer机制,只统计线程真正占用CPU运行的时间,最后把结果写入 jdk.CPUTimeSample 事件里。它对native代码的调用栈归因,也比普通的Java栈采样效果更好,不过这个能力目前本身还是实验性的。

先确认Linux与JDK 25的采集条件
JEP 509的实现范围只覆盖Linux。开始排查之前先确认待排查的进程和你用来分析的工具来自同一个JDK大版本:
uname -s java -version jcmd -l jfr version
如果服务跑在macOS或者Windows上,这个新事件没法按同样逻辑生效,你可以继续用普通的JFR执行采样,别把平台特性差异当成采集失败。jcmd -l 拿到目标PID之后,再启动短时录制操作就好。
用jcmd录一段可复查的CPU样本
没必要上来就改JVM全局启动配置,先给正在运行的JVM建一段短时间的临时录制。下面的配置把 jdk.CPUTimeSample 打开,设置为每个平台线程每消耗20ms CPU时间就采集一个样本:
jfr configure \ --input profile.jfc \ --output /tmp/cpu-profile.jfc \ jdk.CPUTimeSample#enabled=true \ jdk.CPUTimeSample#throttle=20ms jcmdJFR.start settings=/tmp/cpu-profile.jfc \ name=cpu-time-check duration=4m filename=/tmp/cpu-profile.jfr
需要手动终止录制的时候执行 jcmd 。先采几分钟的可复现流量窗口,一般比直接开长时间连续采样更容易控制开销和生成的文件大小。

先看热点方法,再回到业务调用链
录制完成后,直接用JFR内置的筛选视图找自身CPU占用高的方法:
jfr view cpu-time-hot-methods /tmp/cpu-profile.jfr jfr view --verbose cpu-time-hot-methods /tmp/cpu-profile.jfr
第一条命令适合快速做初步筛选。如果返回结果里JSON序列化、压缩、正则匹配或者某个批处理循环排在最前面,再切到JMC里查看完整调用树或者火焰图,确认这个热点是业务方法自身的逻辑耗时,还是被下游调用带出来的附加消耗。
别只盯着方法的占比数字看。这个事件里还包含 eventThread、stacktrace、samplingPeriod 和 biased 等字段;如果返回的调用栈是空的,大概率是采样过程中没能完成栈遍历,不要直接判定成“这个线程没有实际工作”。
throttle怎么选:准确度、开销与丢样一起看
| 设置 | 适合场景 | 主要代价 |
|---|---|---|
10ms | 短时问题复现、热点范围很窄的场景 | 采样更密集,带来的开销和生成的数据文件更大 |
20ms | 单次故障排查窗口的通用平衡配置 | 运行时间极短的方法还是有可能被漏掉 |
500/s | 按所有平台线程统一控制总采样速率 | 线程数量多之后,单线程的采样分辨率会被自动分摊降低 |
JEP 509给出的默认throttle配置是 500/s,JDK自带的 profile.jfc 使用的是 10ms。实际排查的时候建议先用20ms的阈值建立基准,再根据热点结果是否稳定调整参数,不要靠一次短时间的录制就断言某个方法绝对占用了多少比例的CPU。
排查过程中还要同步检查丢样事件:
jfr print --events jdk.CPUTimeSamplesLost /tmp/cpu-profile.jfr
jdk.CPUTimeSamplesLost 里的 lostSamples 会提示内部数据结构打满这类实现层面的约束。如果丢样现象很明显,先把采样频率调低或者把录制窗口缩短,再对比两次的采样结果;不然图表看起来数值很精准,最后得出的结论反而偏差很大。
把CPU结论和延迟结论分开验收
CPU profile反映的是相对CPU周期消耗,不等于请求的总耗时。网络读取、锁等待和线程挂起都可能让一个方法的墙上时钟耗时很长,实际消耗的CPU却很少。最终做优化验证的时候,要同时核对接口延迟、CPU使用率、热点方法占比和服务错误率几个指标。
如果热点集中在压缩或者序列化逻辑,优化数据结构、批量大小和底层算法通常比盲目扩线程效果好很多;如果热点占比很稳定但业务吞吐完全没提升,就要进一步确认是不是被数据库、网络或者锁逻辑限制了。不用急着把采样频率直接拉到最高,先保证每一次录制都覆盖的是同一类流量场景。
常见问题
JFR CPU-Time Profiling在macOS上能用吗?
JEP 509的CPU-time profiling实现是面向Linux平台做的。其他平台可以使用JFR自带的普通执行采样,但不能把这个新事件的特性当成跨平台通用的保证。
jdk.CPUTimeSample默认会采集吗?
不会。该事件默认没有启用,需要在JVM启动参数或者JFC配置文件里显式打开才能生成对应的采样数据。
throttle越小是不是一定越准确?
更密集的采样通常能提升短时间窗口内的采样分辨率,但也会增加采样本身的开销和生成的数据量,还可能引发丢样。要以同一流量下多次重复得到的一致结果作为判断依据。
为什么CPU热点最高的方法不是最慢接口?
因为接口总耗时包含大量等待时间,而CPU profile只统计线程实际消耗的CPU cycles。需要把JFR热点数据和接口延迟、全链路调用链放到一起对照分析,才能得到完整结论。
收尾检查清单
一次合格的Java 25 CPU耗时排查,至少要留下Linux系统与JDK版本信息、JFR具体配置、录制时长、throttle参数、热点视图截图和丢样检查结果。只有在相同流量窗口下多次重复得到相近的热点分布,并且优化上线后CPU占用、接口延迟和错误率同步改善,再把对应的优化结论合入生产环境。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习