CPU 飙高排查
问题
生产环境 CPU 直接飙到 100%,如何快速定位和解决?
答案
排查四步法
第一步:找到高 CPU 进程
top 查看进程 CPU 排名
top -c
# 按 P 键按 CPU 排序
# 找到占用 CPU 最高的 Java 进程 PID,假设是 12345
第二步:找到高 CPU 线程
查看进程内线程 CPU 排名
top -Hp 12345
# 找到 CPU 最高的线程 TID,假设是 12378
# 将线程 TID 转为十六进制(jstack 用十六进制)
printf "%x\n" 12378
# 输出 305a
第三步:导出线程堆栈
jstack 导出线程栈
jstack 12345 > thread_dump.txt
# 搜索十六进制线程 ID
grep -A 30 "nid=0x305a" thread_dump.txt
第四步:分析堆栈
线程堆栈示例
"pool-1-thread-3" #15 prio=5 os_prio=0 tid=0x00007f nid=0x305a runnable
java.lang.Thread.State: RUNNABLE
at com.example.service.OrderService.calculateDiscount(OrderService.java:156)
at com.example.service.OrderService.processOrder(OrderService.java:89)
...
线程状态 RUNNABLE + 一直在执行某个方法 → 定位到具体的代码行。
常见 CPU 飙高原因
| 原因 | 表现 | 解决 |
|---|---|---|
| 死循环 | RUNNABLE,堆栈固定在某一行 | 修复循环退出条件 |
| 正则回溯 | RUNNABLE,在 Pattern.matcher | 优化正则或加超时 |
| 频繁 Full GC | 多个 GC 线程 RUNNABLE | 排查内存问题 |
| 大量线程竞争 | BLOCKED 线程多 | 减小锁粒度 |
| 序列化/反序列化 | CPU 密集型计算 | 换高性能方案 |
| 加密/压缩 | CPU 密集型计算 | 异步化或硬件加速 |
使用 Arthas 一键排查
Arthas thread 命令
# 启动 Arthas
java -jar arthas-boot.jar
# 查看最忙的 N 个线程
thread -n 3
# 查看线程占用 CPU 排名
thread --all
# 查看死锁
thread -b
Arthas profiler(火焰图)
# 生成 CPU 火焰图,直观看到热点方法
profiler start
# 等待 30 秒
profiler stop --format html --file /tmp/cpu-profile.html
小技巧
- 多次
jstack对比:同一个线程如果每次都卡在同一行 → 死循环 jstack建议连续导出 3-5 次,每次间隔几秒
正则回溯导致 CPU 100% 案例
有问题的正则
// 贪婪匹配 + 回溯 → 灾难性回溯
String regex = "(a+)+b";
String input = "aaaaaaaaaaaaaaaaaaaaaaaac"; // 不匹配但会疯狂回溯
input.matches(regex); // CPU 100%
解决方案
// 1. 使用独占量词(禁止回溯)
String regex = "(a++)b";
// 2. 加执行超时
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Boolean> future = executor.submit(() -> input.matches(regex));
try {
future.get(1, TimeUnit.SECONDS); // 1 秒超时
} catch (TimeoutException e) {
future.cancel(true);
log.warn("正则匹配超时");
}
常见面试问题
Q1: CPU 100% 排查步骤是什么?
答案:
四步法:top 找进程 → top -Hp 找线程 → printf "%x" 转十六进制 → jstack 匹配 nid 看堆栈。
Q2: 如何区分是业务代码还是 GC 导致的 CPU 高?
答案:
- GC 导致:
jstack中大量GC Thread为 RUNNABLE;jstat -gcutil显示 Full GC 频繁 - 业务代码:用户线程 RUNNABLE,堆栈指向业务代码
- 也可以看
top -Hp中高 CPU 线程的名字,GC 线程名包含 "GC"
Q3: 如何预防 CPU 飙高?
答案:
- 正则表达式加超时保护
- 循环中加退出条件 / 最大迭代次数
- 合理的 JVM 内存配置(减少 Full GC)
- 线程池合理配置(避免线程过多竞争)
- 上线前压测,提前发现热点