Skip to content

线上 CPU 飙升 100%?一整套标准排查 SOP

线上 CPU 飙升 100%?一整套标准排查 SOP

Section titled “线上 CPU 飙升 100%?一整套标准排查 SOP”

在分布式系统运维中,最让人肾上腺素飙升的报警莫过于:“告警!生产节点 app-node-01 CPU 使用率达到 99%!”。

这时候如果你重启大法好,不仅销毁了犯罪现场,由于请求流量依然会打过来,五分钟后另一台机器也会崩掉。

作为一名成熟的 Java 架构师或高级开发,你必须形成肌肉记忆,熟练掌握以下这套从操作系统到 JVM 字节码层面的标准排查 SOP (Standard Operating Procedure)

阶段一:传统命令流(原汁原味的 Linux 捉虫)

Section titled “阶段一:传统命令流(原汁原味的 Linux 捉虫)”

哪怕在 2026 年,很多极其安全的内网环境是不允许你随便安装外部诊断工具的。这就要求你必须会用系统自带的工具。

登录服务器,直接输入 top,按 P(按 CPU 排序)。 你会瞬间看到占用最高的 PID(假设是 10245)。如果是 Java 进程,那好戏开场了。

Step 2: 揪出进程里最作妖的线程 (top -Hp)

Section titled “Step 2: 揪出进程里最作妖的线程 (top -Hp)”

一个 Java 进程里可能有几百个线程,到底是哪个在死循环? 运行:

Terminal window
top -Hp 10245

这一步会列出这个进程下的所有线程。假设你发现线程 ID 10250 一直占着 90% 的 CPU。

将那个耗 CPU 的线程 ID(10250)转换为十六进制,因为一会的 JVM 栈快照里,线程 ID 都是十六进制显示的。

Terminal window
printf "%x\n" 10250
# 输出: 280a

Step 4: 打印线程快照并定位代码 (jstack)

Section titled “Step 4: 打印线程快照并定位代码 (jstack)”

直接把该进程的所有线程堆栈导出,并使用 grep 定位刚才那个罪魁祸首:

Terminal window
jstack 10245 | grep -A 20 "280a"

这时候,终端里会赫然打印出: at com.company.utils.RegexUtil.match(RegexUtil.java:45) 真相大白。

阶段二:现代诊断降维打击(使用 Arthas)

Section titled “阶段二:现代诊断降维打击(使用 Arthas)”

在大多数情况下,我们推荐直接使用阿里开源的 Java 诊断神器 Arthas。它的能力简直是降维打击。

如果 CPU 飙高,直接运行 Arthas 并进入控制台。只需敲一个命令:

Terminal window
dashboard

或者直接找出当前 CPU 占用前 3 名的线程,并打印出它们的堆栈:

Terminal window
thread -n 3

屏幕上直接就会出现引发飙高的具体代码行数,省略了上面传统做法的繁琐步骤。

即便你找到了是哪行代码在跑,你还需要知道为什么。通常导致 CPU 打满的元凶有以下三种:

1. 业务代码的死循环或低效算法

Section titled “1. 业务代码的死循环或低效算法”

最典型的是 while(true) 缺乏终止条件,或者在巨大的数据集合里进行了 O(N^2) 级别的双层 for 循环嵌套。 特征:堆栈定位到业务的具体 Controller 或 Service 行数。

2. 正则表达式引发的回溯灾难 (Catastrophic Backtracking)

Section titled “2. 正则表达式引发的回溯灾难 (Catastrophic Backtracking)”

如果你写了一个类似 ^(a+)+$ 这种嵌套量词的正则表达式,并且让它去匹配一段恶意构造的长文本,引擎会陷入指数级的尝试匹配中,瞬间锁死单核 CPU。 特征:堆栈卡在 java.util.regex.Pattern.match 等深层方法中。 解法:使用 RE2J 等无回溯风险的正则引擎,或优化表达式,禁止用户输入过长文本。

3. 垃圾回收器的死亡挣扎 (GC Thrashing)

Section titled “3. 垃圾回收器的死亡挣扎 (GC Thrashing)”

这是最容易被误判的一种情况。 你通过 jstack 发现,占用 CPU 的并不是你的业务线程,而是叫 GC task thread#0 的线程。

这意味着你的程序其实没有死循环,而是发生 OOM(内存泄漏)的前兆! 内存快满了,JVM 试图进行 Full GC 来释放空间。但每次 STW 扫描了半天,只释放了 1% 的内存。下一秒又满了,又开始扫。CPU 全耗在垃圾回收上了。

解法:这不再是 CPU 的问题了,请立刻转入 OOM 内存排查流程(参考专栏下一篇《OOM 内存泄漏溯源实战》),去扒一扒到底是谁塞了几百万个对象在内存里不释放。

处理线上 CPU 报警,记住八字真言:先找线程,再看堆栈。 不要慌乱猜测,不要盲目重启。通过 jstackArthas 拿到那份带着行号的快照,你就掌握了修复故障的最强底气。