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 或 Arthas thread -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 更快」

状态

  • 已背速记(命令序列)
  • 能讲通俗版
  • 能答追问