一、CPU飙升的本质:线程层面的资源争抢

当服务器CPU使用率飙升至100%,其本质是某个或多个线程持续占用CPU资源而不释放。这种现象通常源于以下几类问题:

无限循环:代码中存在没有退出条件的循环结构

频繁GC:垃圾回收线程持续运行,占用大量CPU

锁竞争:线程因锁争用而不断自旋重试

正则回溯:复杂正则表达式在极端输入下陷入灾难性回溯

排查的核心逻辑可以概括为三个关键步骤:

1. 定位进程:找到占用CPU最高的Java进程(PID)

2. 揪出线程:在目标进程中找出最繁忙的线程(TID)

3. 追踪代码:将线程ID映射到具体代码行,分析堆栈信息

下文将基于两款实战工具——JDK原生`jstack`与阿里开源`Arthas`,完整演示这一排查过程。

二、工具一:jstack——JDK原生堆栈分析工具

`jstack`是JDK自带的轻量级线程堆栈打印工具,无需额外安装。虽然操作步骤稍显繁琐,但它是理解JVM线程模型的基础方案,也是面试中考察底层功底的常见考点。

2.1 步骤1:定位高CPU进程(PID)

登录服务器后,执行`top`命令,观察进程CPU占用列(%CPU):

```bash

top

```

输出示例:

```

 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND

32208 root      20   0 4567892 345672  12344 S  99.7  2.3   1256:34 java

```

找到CPU占用接近100%的Java进程,记录其PID(本例中为`32208`)。

类比理解:如同在工厂中寻找“机器轰鸣最响的车间”,这个PID即为问题进程的标识。

2.2 步骤2:定位高CPU线程(TID)

基于进程PID,进一步查看其内部线程的CPU占用情况:

```bash

top Hp 32208

```

输出示例:

```

 PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND

32230 root      20   0 4567892 345672  12344 R 99.5  2.3   1245:34 java

```

记录下CPU占用最高的线程PID(此处为`32230`),这个十进制数值即线程ID(TID)。

类比理解:进入问题车间,找到“最忙的工人”,此TID即为该工人的工号。

2.3 步骤3:转换线程ID并打印堆栈

`jstack`输出的线程标识`nid`为十六进制格式,需将十进制TID转换:

```bash

printf "%x\n" 32230

```

输出示例:

```

7de6

```

接着,使用`jstack`打印进程堆栈,并过滤出目标线程的上下文:

```bash

jstack l 32208 | grep 7de6 A 10

```

输出示例:

```

"pool1thread1" 10 prio=5 os_prio=0 tid=0x00007fb4fc16e000 nid=0x7de6 runnable

  java.lang.Thread.State: RUNNABLE

       at com.tuling.learnjuc.demo.JVMCPU$CPUTask.run(JVMCPU.java:16)

       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

       at java.lang.Thread.run(Thread.java:750)

```

2.4 堆栈信息解读要点

线程状态:`RUNNABLE`表示线程正在执行代码,是CPU飙升的典型状态;若为`BLOCKED`或`WAITING`,则通常不是CPU问题的直接原因。

nid:十六进制线程ID,用于与`top Hp`找到的线程匹配。

调用栈顶:最上方的`at`行直接指向问题代码的类、方法及行号(本例中为`JVMCPU.java:16`),即需要深入分析的故障点。

类比理解:查看“最忙工人的工作记录”,发现他反复执行第16行的任务,这正是问题的根源。

三、工具二:Arthas——阿里开源诊断利器

`Arthas`是一款功能强大的Java诊断工具,提供交互式命令行界面,简化了线程分析流程,尤其适合生产环境快速定位问题。

3.1 步骤1:启动Arthas并选择进程

下载并启动Arthas:

```bash

curl O https://arthas.aliyun.com/arthasboot.jar

java jar arthasboot.jar

```

启动后,Arthas会列出所有Java进程,输入目标进程前的序号(例如`1`对应PID 32208)即可进入控制台。

3.2 步骤2:使用dashboard查看线程CPU占用

在Arthas控制台中执行`dashboard`命令:

```bash

dashboard

```

界面将实时展示各线程的CPU使用率,可直接观察到最繁忙的线程(如`pool1thread1`占用99.68%)。

3.3 步骤3:使用thread命令定位代码行

执行`thread n 1`打印CPU占用最高的线程堆栈:

```bash

thread n 1

```

输出示例:

```

"pool1thread1" Id=10 cpuUsage=99.68% deltaTime=202ms time=1225390ms RUNNABLE

   at com.tuling.learnjuc.demo.JVMCPU$CPUTask.run(JVMCPU.java:16)

   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

   at java.lang.Thread.run(Thread.java:750)

```

优势:无需手动转换线程ID,输出直接包含代码行号,操作路径更短。

四、问题定位后的常见解决方案

找到问题代码后,需根据具体场景采取相应优化措施:

问题类型 典型表现 解决方案
死循环 代码中存在`while(true)`无退出条件 增加循环控制变量或超时退出机制
频繁GC GC线程占用大量CPU,伴随内存波动 优化对象生命周期,使用对象池,调整堆内存参数
锁竞争 大量线程处于`BLOCKED`状态 减少锁粒度,使用并发容器,避免热点锁
正则回溯 特定输入导致正则计算量爆炸 优化正则表达式,设置匹配超时,使用非回溯引擎

五、总结

CPU飙升问题的排查本质是“进程→线程→代码行”的三层溯源过程。无论是使用JDK原生的`jstack`,还是更高效的`Arthas`,核心思路始终一致:

1. 从`top`命令入手,定位高CPU进程

2. 深入进程内部,找出最繁忙的线程

3. 分析线程堆栈,锁定问题代码行

掌握这一分析框架,不仅能快速解决线上故障,更能加深对JVM线程模型与操作系统调度机制的理解,为后续的性能调优与架构设计奠定基础。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐