跳过导航

一次 Java 生产环境 CPU 100% 的完整排查过程

约 4 分钟...次浏览
专栏分布式稳定性与工程实践第 5 篇

CPU 100% 不是根因,它只说明某个时间窗口计算资源被耗尽。可能是业务流量、死循环、正则回溯、序列化、频繁 GC、锁自旋或系统调用。正确流程是先保留现场,再把“主机 CPU”映射到“进程—线程—代码栈”。

事故现场

某 API P99 从 120ms 升至 8s,单个 Java 实例 CPU 占满,其他实例约 40%。先摘除异常实例部分流量,避免完全杀进程丢失证据,同时确认整体容量足够。记录告警时间、发布版本、请求量和实例 ID。

Linux 上初筛:

pidstat -p <pid> 1
top -H -p <pid>

找到占用最高的线程十进制 TID,例如 28731,转换为十六进制:

printf '%x\n' 28731
jcmd <pid> Thread.print -l > /tmp/jstack-1.txt

在 dump 中搜索 nid=0x703bjcmd 应使用与目标 JVM 相同的用户和兼容 JDK 工具,并在容器 PID namespace 内确认 PID;attach 失败时不要反复高频尝试。间隔 5–10 秒采集三次,持续出现在相同 RUNNABLE 栈帧的线程更有价值。一次快照可能刚好采到偶发任务。

这套 TID → nid 映射只适合平台线程。虚拟线程并不与操作系统线程一一对应,top -H 看到的是载体线程;Java 21+ 可在受控路径使用 jcmd <pid> Thread.dump_to_file -format=json <file> 获取包含虚拟线程层次的转储,并结合 JFR 与应用级任务标签分析。不能把热点载体线程误认成某一个固定虚拟线程。

先排除 GC

jcmd <pid> GC.heap_info

优先结合持续采集的 GC 日志、JFR 和 JVM 指标判断;需要时再短时间使用 jstat -gcutil <pid> 1000 10jstat 的列含义和可用性会随收集器/JDK 变化,不能只凭某一列下结论。若 Young GC 极其频繁或完整/退化回收连续发生,CPU 可能花在回收而非业务代码。不要看到 GC 线程就立即扩大堆,更大的堆可能延长回收。

栈定位到正则回溯

多次栈均落在 java.util.regex.Pattern,上层是日志脱敏方法。新版本正则对长且不匹配的输入产生灾难性回溯。用生产脱敏样本在隔离环境复现,并使用 JFR/async-profiler 验证 CPU 火焰图:

jcmd <pid> JFR.start name=cpu settings=profile duration=60s filename=/tmp/cpu.jfr

JFR 有开销但通常可控,仍应先评估并限制时长。敏感参数可能进入录制文件,应按生产数据管理。

async-profiler 适合观察 CPU、分配和锁热点,但命令参数随版本与打包方式变化,应使用组织批准的二进制并查看该版本帮助。硬件计数器、内核栈或 wall-clock profile 可能需要额外权限;不要为了临时诊断给生产容器永久增加 privileged 权限。

修复使用有边界的字符类或线性扫描替代回溯正则,并增加超长输入限制。压测对比相同输入分布下 CPU、吞吐和 P99,再灰度发布。

Arthas 快速方法

thread -n 10
thread <id>
profiler start
profiler stop --format html

动态诊断工具应限制权限,避免在高负载时执行大范围 trace/watch,表达式求值和对象展开可能进一步伤害实例。

容易误判的情况

  • CPU 高但吞吐同步增长,可能只是容量不足,不是代码 Bug。
  • 线程 BLOCKED 多通常不是 CPU 直接来源,但锁竞争可能伴随自旋和吞吐下降。
  • 容器显示 100% 可能只是用满一个核,应核对 CPU quota 和宿主机口径。
  • 在 cgroup 限额内,CPU throttling 会制造延迟尖峰;“使用率不满宿主机”不代表容器还有配额。
  • dump 前重启虽然恢复服务,却失去根因证据。

生产检查清单

  • 是否保留线程栈、GC、流量、版本和容器限额?
  • 是否多次采样并将 TID 正确转换为十六进制 nid?
  • 是否区分业务计算与 GC CPU?
  • Profile 是否限时、评估开销并保护敏感数据?
  • 修复是否用故障输入和真实并发验证?
  • 是否增加 CPU 饱和、线程池队列和自动摘流机制?

高 CPU 排查的核心是关联:同一时间、同一实例、同一线程、同一代码栈,最终用可复现实验闭环。

分享:
文章作者:狼码纪
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。文章可能参考了其他优秀文章,如有侵权请联系删除。