线上 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 年,很多极其安全的内网环境是不允许你随便安装外部诊断工具的。这就要求你必须会用系统自带的工具。
Step 1: 找出捣乱的进程 (top)
Section titled “Step 1: 找出捣乱的进程 (top)”登录服务器,直接输入 top,按 P(按 CPU 排序)。
你会瞬间看到占用最高的 PID(假设是 10245)。如果是 Java 进程,那好戏开场了。
Step 2: 揪出进程里最作妖的线程 (top -Hp)
Section titled “Step 2: 揪出进程里最作妖的线程 (top -Hp)”一个 Java 进程里可能有几百个线程,到底是哪个在死循环? 运行:
top -Hp 10245这一步会列出这个进程下的所有线程。假设你发现线程 ID 10250 一直占着 90% 的 CPU。
Step 3: 十六进制转换
Section titled “Step 3: 十六进制转换”将那个耗 CPU 的线程 ID(10250)转换为十六进制,因为一会的 JVM 栈快照里,线程 ID 都是十六进制显示的。
printf "%x\n" 10250# 输出: 280aStep 4: 打印线程快照并定位代码 (jstack)
Section titled “Step 4: 打印线程快照并定位代码 (jstack)”直接把该进程的所有线程堆栈导出,并使用 grep 定位刚才那个罪魁祸首:
jstack 10245 | grep -A 20 "280a"这时候,终端里会赫然打印出:
at com.company.utils.RegexUtil.match(RegexUtil.java:45)
真相大白。
阶段二:现代诊断降维打击(使用 Arthas)
Section titled “阶段二:现代诊断降维打击(使用 Arthas)”在大多数情况下,我们推荐直接使用阿里开源的 Java 诊断神器 Arthas。它的能力简直是降维打击。
如果 CPU 飙高,直接运行 Arthas 并进入控制台。只需敲一个命令:
dashboard或者直接找出当前 CPU 占用前 3 名的线程,并打印出它们的堆栈:
thread -n 3屏幕上直接就会出现引发飙高的具体代码行数,省略了上面传统做法的繁琐步骤。
阶段三:常见 CPU 飙升元凶分析
Section titled “阶段三:常见 CPU 飙升元凶分析”即便你找到了是哪行代码在跑,你还需要知道为什么。通常导致 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 报警,记住八字真言:先找线程,再看堆栈。
不要慌乱猜测,不要盲目重启。通过 jstack 或 Arthas 拿到那份带着行号的快照,你就掌握了修复故障的最强底气。