CPU 100% 完整排查命令序列
一句话速记
Java 进程 CPU 100% = 找到最耗 CPU 的线程 → 对应到 Java 线程名 → 看它在干什么。标准流程:top -Hp <pid> 找高 CPU 线程 TID → 转 16 进制 → jstack 里搜对应 nid → 看 stacktrace;或直接用 Arthas thread -n 3 一键输出最耗 CPU 的 3 条线程栈。
通俗解释(5 分钟版)
CPU 100% 的常见原因:
| 原因 | 典型栈特征 |
|---|---|
| 死循环 / 无限递归 | while(true) 或同一个方法反复出现 |
| Full GC 频繁 | 大量 GC Thread / VM Thread 高 CPU |
| JIT 编译线程飙高 | C2 CompilerThread 高 CPU |
| 正则表达式回溯 | java.util.regex.Pattern 深栈 |
| 大量线程同时计算 | 正常但业务并发太高 |
关键细节
1)标准排查序列(非 Arthas 版)
# Step 1: 找到 Java 进程 PID
jps -l
# 或:ps aux | grep java
# Step 2: 找到最耗 CPU 的线程(TID 是十进制)
top -Hp <pid>
# 按 P 键按 CPU 排序,记录最高几个 TID
# Step 3: TID 转 16 进制
printf '%x\n' <TID>
# 例:TID=12345 → 0x3039
# Step 4: dump 线程栈
jstack <pid> > /tmp/jstack.txt
# 在 jstack 输出里搜 nid=0x3039
grep -A 30 'nid=0x3039' /tmp/jstack.txt
# Step 5: 分析 stacktrace 定位问题2)Arthas 一键版(推荐)
# 安装并启动 Arthas(已在机器上)
java -jar arthas-boot.jar <pid>
# 最耗 CPU 的前 3 个线程(直接输出栈)
thread -n 3
# 找所有状态为 BLOCKED 的线程(锁竞争排查)
thread --state BLOCKED
# 找死锁
thread -b
# 持续监控特定线程
thread <thread_id>Arthas thread -n 3 输出示例:
"http-nio-8080-exec-1" Id=24 cpuUsage=43.21% deltaTime=433ms time=892ms RUNNABLE
at java.util.regex.Pattern$Branch.match(Pattern.java:4272)
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
...(深栈 → 正则回溯死循环)
"C2 CompilerThread0" Id=6 cpuUsage=35.12% deltaTime=351ms time=12043ms RUNNABLE
...(JIT 编译,通常短时间正常)
3)GC 线程高 CPU 的判断
# jstack 里 GC 线程的线程名
"GC Thread#0" → 并行 GC 线程
"VM Thread" → JVM 内部 STW 操作(GC/Deopt 等)
"Concurrent Mark-Sweep GC Thread" → CMS 并发线程
# 同时看 GC 情况
jstat -gcutil <pid> 1000 # 每 1 秒打印一次 GC 统计
# 关注 FGC 列:每秒增长 → Full GC 频繁判断是 GC 导致 CPU 高的方式:
jstat -gcutil看到 FGC 快速增长top -Hp里高 CPU 线程名含 GC/VM- 后续排查:堆快照分析 + 解决内存泄漏/大对象问题
4)JIT 编译线程高 CPU
# 线程名特征
"C1 CompilerThread0" → 客户端编译器
"C2 CompilerThread0" → 服务端编译器(主力)
# 现象:服务刚启动或刚部署后 CPU 高,持续几分钟后降下来
# 原因:JVM 在 JIT 编译热点方法
# 这是正常现象!
# 如果 C2 线程持续高(不是启动期):
# 可能是某些方法触发了"去优化"(Deoptimization)反复编译
# 排查:-XX:+PrintCompilation 打印编译事件,看是否有循环编译同一方法5)正则表达式回溯死循环(高频坑)
// 危险的正则(在输入不匹配时会指数级回溯)
Pattern p = Pattern.compile("(a+)+$");
p.matcher("aaaaaaaaaaaaaaaaaaaaaaab").matches();
// 上面的调用会死循环!CPU 100%
// 根因:嵌套量词 + 回溯机制排查:thread -n 看到深栈里全是 Pattern.match() → 检查代码里的正则 → 修改为原子组或限制回溯。
Arthas 追踪:
watch java.util.regex.Pattern match '{params[0]}' # 看传入的字符串6)常见 CPU 高的原因速查表
线程名/栈特征 原因 建议
─────────────────────────────────────────────────────────────────
业务线程 + while/for 无终止 死循环 Review 代码
Pattern.match 深栈 正则回溯 修改正则
GC 相关线程高 + jstat FGC 增长 Full GC 频繁 解决内存泄漏
C2 CompilerThread 启动后高 正常 JIT 热身 等几分钟
所有线程 CPU 均匀高 正常高并发 加机器/限流
BLOCKED 线程多 锁竞争 thread -b 找锁
7)jstack + grep 快速过滤
# 找所有 RUNNABLE 状态的线程(不包含 WAITING)
grep -E 'java.lang.Thread.State: RUNNABLE' /tmp/jstack.txt | wc -l
# 找所有 BLOCKED 线程
grep -B 10 'java.lang.Thread.State: BLOCKED' /tmp/jstack.txt
# 统计各状态线程数
grep 'java.lang.Thread.State:' /tmp/jstack.txt | sort | uniq -c延伸追问
- Q:有时候
top -Hp看不到 Java 线程,怎么办? →top -Hp的线程名可能被截断;改用ps -mp <pid> -o THREAD,tid,time | sort -rn | head -20看更完整的线程信息。Arthas 是最省事的。 - Q:CPU 持续 100% vs 偶发 100%,排查方法有区别吗?
→ 持续 100%:直接
jstack或 Arthasthread -n可抓到现场。偶发 100%:需要在高峰时多次jstack(间隔 1-3 秒,连续 3 次),找到连续出现的栈帧——那就是热点。 - Q:
jstack输出里 WAITING 的线程很多正常吗? → 正常——WAITING 是线程在等待任务(线程池空闲)或 I/O,不耗 CPU。关注 RUNNABLE 和 BLOCKED。
我的记法
- 三步排查:
top -Hp找 TID →printf '%x'转 16 进制 →jstack grep nid - 更快:Arthas
thread -n 3一键搞定 - 特殊类型:GC 线程高(看 jstat FGC)/ JIT 高(启动正常)/ 正则回溯(Pattern 深栈)
- 死锁:Arthas
thread -b - BLOCKED 线程多 = 锁竞争,RUNNABLE 高才是真 CPU 消耗
- 一句话:「TID 转 16 进制 + jstack grep nid = 定位在干什么,Arthas thread -n 更快」
状态
- 已背速记(命令序列)
- 能讲通俗版
- 能答追问