跳转到内容

CPU过高排查

💡

CPU飙高是线上最常见的事故之一。通常由死循环、死锁、GC频繁、正则回溯、大JSON序列化等原因导致。本文提供完整详细的排查流程,每一步都有具体命令、输出示例和解读。


一、排查总流程(5步法)


二、详细步骤演示(每条命令可复制执行)

第1步:定位高CPU进程

📌

使用 top 命令查看所有进程,按CPU使用率排序,找到占用最高的进程。

关键判断:

  • %CPU持续>90%:确认CPU飙高,需要排查
  • load average超过CPU核数:系统过载,可能有大量线程在等待CPU
  • 同时关注%MEM:如果内存也高,可能是GC频繁导致的CPU高

第2步:定位高CPU线程

📌

找到进程后,用 top -Hp [PID] 查看该进程内所有线程的CPU使用情况。

判断要点:

  • 如果只有一个线程CPU极高(95%+) → 死循环或复杂计算
  • 如果多个线程CPU都高(各占30-50%) → 并发问题或GC线程
  • 如果GC线程CPU高 → 内存不足,频繁GC

第3步:线程ID转16进制

📌

jstack中线程的nid(native ID)是16进制显示的,需要将第2步拿到的十进制TID转为16进制。

得到16进制线程ID:0x3065(jstack中用这个值匹配)

第4步:jstack获取线程堆栈

最关键的一步!jstack打印JVM中所有线程的堆栈信息。通过16进制线程ID定位到具体的线程,查看它正在执行什么代码。

第5步:根据堆栈定位到代码

📌

拿到堆栈信息后,打开对应的Java文件,定位到问题代码行,分析为什么导致CPU高。


三、Arthas快速定位(推荐,替代传统jstack)

Arthas是阿里开源的Java诊断神器。传统jstack需要多步操作,Arthas一条命令直接给出CPU最高的线程堆栈。强烈推荐在生产环境使用。


四、常见CPU飙高场景与解决方案

4.1 死循环

特征

解决

线程状态:RUNNABLE 堆栈:停在while(true)行 多次dump:堆栈完全不变

检查循环退出条件;加超时机制;循环体内加Thread.sleep/yield;使用有界队列+阻塞操作替代空转

4.2 死锁

特征

解决

线程状态:BLOCKED jstack末尾:Found 1 deadlock 多个线程互相等待对方的锁

统一加锁顺序;使用tryLock(timeout)替代lock();减小锁粒度;用jstack -l查看锁持有者

4.3 频繁GC(内存问题导致CPU高)

特征

解决

GC线程CPU占用高 jstat -gc 显示FGC持续增长 内存使用率接近100%

jmap -histo查看对象分布→定位大对象;调大堆内存 -Xms -Xmx;排查内存泄漏(ThreadLocal没remove/静态集合无限增长)

4.4 正则表达式回溯

4.5 HashMap并发死循环(JDK7)

💡

JDK7中HashMap并发扩容时头插法导致环形链表,get操作进入死循环。堆栈停在HashMap.transfer()。解决:直接改用ConcurrentHashMap。

4.6 大JSON序列化/大对象创建

  • 特征:堆栈停在Jackson/Gson/Fastjson序列化方法
  • 原因:返回数据量过大(百万级记录)、循环引用、嵌套对象过深
  • 解决:分页返回、@JsonIgnore忽略不必要字段、使用Streaming API逐条写、限制最大返回条数

五、完整排查命令速查表

命令

作用

top

查看所有进程CPU使用率,找到高CPU进程

top -Hp [PID]

查看进程内每个线程的CPU使用率

printf "%x\n" [TID]

线程ID十进制→十六进制

jstack [PID] | grep [nid] -A 30

查看线程堆栈,定位问题代码

jstack -l [PID]

查看线程堆栈+锁持有信息

jstat -gc [PID] 1000

每秒查看GC情况

jmap -histo:live [PID]

查看内存对象分布

jmap -dump:live,file=xxx.hprof [PID]

导出堆dump文件

Arthas: thread -n 3

一键看CPU最高的3个线程堆栈

Arthas: dashboard

实时监控面板

Arthas: thread -b

检测死锁

vmstat 1 10

系统级CPU/内存/IO监控


排查口诀:先 top 看进程 → top -Hp 看线程 → 转 16 进制 → jstack 拿堆栈 → 定位代码行 → Arthas 深入分析。传统方式5分钟定位,Arthas方式1分钟定位。