CPU飙高排查
Java 进程 CPU 飙高是常见线上问题。排查核心是找到哪个线程在消耗 CPU,再定位它正在执行什么 Java 代码。
# 1. 排查步骤
基本步骤:
- 找到 Java 进程 ID。
- 找到高 CPU 线程 ID。
- 转换线程 ID 为十六进制。
- 在线程 dump 中找到对应线程。
- 分析调用栈。
- 结合业务日志和监控确认原因。
# 2. 找 Java 进程
jps -l
或者:
ps -ef | grep java
假设进程 ID 是 12345。
# 3. 找高 CPU 线程
top -Hp 12345
找到 CPU 占用高的线程 ID,例如 12360。
转换为十六进制:
printf "%x\n" 12360
假设结果是:
3048
# 4. 抓线程栈
jstack -l 12345 > thread.dump
搜索:
nid=0x3048
查看该线程调用栈。
# 5. 常见原因
CPU 高常见原因:
- 死循环。
- 复杂正则。
- 大集合排序或遍历。
- JSON 序列化大对象。
- 加解密或压缩。
- 频繁 GC。
- 锁竞争导致自旋。
- 日志量过大。
- 热点接口流量突增。
# 6. GC 导致 CPU 高
如果 CPU 高同时 GC 频繁,要看 GC 日志。
表现:
- GC 线程占用 CPU。
- 应用吞吐下降。
- 老年代持续增长。
- Young GC 或 Full GC 频繁。
这时需要分析对象分配和内存泄漏,而不是只看业务线程栈。
# 7. 多次采样
不要只抓一次线程栈。建议连续抓 3 到 5 次。
如果同一个线程总在同一段代码,说明它可能卡在热点逻辑。
如果每次不同,可能是整体负载高或采样不稳定,需要结合 JFR 或 profiler。
# 8. 阶段小结
CPU 飙高排查的关键链路是:进程 -> 线程 -> 十六进制 nid -> Java 调用栈 -> 业务原因。不要只看进程 CPU,也不要只猜 GC 或死循环,要用线程栈和监控建立证据链。
# 9. CPU 高原因分类
| 类型 | 特征 | 证据 |
|---|---|---|
| 业务死循环 | 单线程长期高 CPU | 多次 jstack 同一栈 |
| 复杂计算 | CPU 高但逻辑正常 | profiler 热点方法 |
| GC 繁忙 | GC 线程占 CPU | GC 日志频繁 |
| 锁自旋 | CPU 高且锁竞争 | JFR/线程栈 |
| 正则回溯 | 栈中 regex 方法 | 输入样本和正则 |
| 序列化大对象 | JSON/序列化方法热 | JFR 分配和 CPU |
# 10. 排查命令串
jps -l
top -Hp <pid>
printf "%x\n" <tid>
jstack -l <pid> > thread.dump
如果 jstack 不能明确定位,继续:
jcmd <pid> JFR.start name=cpu settings=profile filename=/tmp/cpu.jfr duration=120s
# 11. Tips 快问快答
Q:CPU 高是不是一定要重启?
A:不一定。先判断是否影响服务和能否限流、隔离、关闭异常任务。重启前尽量保留线程栈或 JFR。
Q:单核满和整机满区别大吗?
A:很大。单核满可能是单线程死循环,整机满可能是并发流量、GC 或计算任务。
Q:为什么 CPU 高但线程栈看不出变化?
A:可能采样点不准、热点在 native、JIT 编译代码或多个线程分摊,需要 profiler。
Q:GC 导致 CPU 高怎么确认?
A:看 GC 日志频率、GC 线程 CPU、JFR GC 事件和应用吞吐下降。
上次更新: 2026/06/24, 16:03:01