程序死循环是Linux服务器CPU飙升最常见根因,排查需四步:先用top确认%us持续>70%且%wa≈0;再top -H -p PID定位高CPU线程并printf "%x"转十六进制;接着jstack导出堆栈并grep该线程ID,查RUNNABLE状态及重复调用行;最后用perf采样验证热点是否在业务方法。

程序死循环是导致 Linux 服务器 CPU 飙升最常见、也最容易定位的根因之一。它通常表现为某个进程的 %CPU 持续接近 100%,而系统其他资源(内存、IO、网络)相对平稳。排查关键不在于“找高 CPU”,而在于“确认是不是真在循环跑,且循环卡在用户代码里”。
第一步:确认是用户态忙,不是假象或系统瓶颈
登录服务器后先执行 top,重点看底部这一行:
%Cpu(s): 89.2 us, 5.1 sy, 0.0 wa, 6.7 id
如果 us(user)占比长期高于 70%,同时 wa(iowait)接近 0、sy(system)不高,基本可锁定为应用程序自身逻辑问题——死循环就藏在这里。此时 load average 可能不高(比如单线程死循环,4 核机器 load=1.2 但 CPU 却 100%),所以不能只看 load。
第二步:定位到具体 Java 进程和高负载线程
在 top 中按 P(大写)按 CPU 使用率排序,记下 PID(如 12345)。然后用线程视角查看:
-
top -H -p 12345—— 切换到线程模式,找到 %CPU 最高的那个 TID(比如 12350) -
printf "%x\n" 12350—— 将十进制线程 ID 转为小写十六进制(输出 303e)
注意:不要用 echo "obase=16;12350" | bc,它输出大写且可能带换行;printf 更准、更常用。
第三步:抓取线程堆栈并匹配循环线索
导出 Java 进程全部线程快照:
jstack 12345 > thread.log
然后搜索刚才的十六进制线程名 303e:
grep -A 20 "303e" thread.log
重点关注两个信号:
- 线程状态是 RUNNABLE(不是 WAITING 或 BLOCKED)
- 堆栈末尾反复出现同一方法调用,例如:
at com.example.util.TimeUtils.nowMs(TimeUtils.java:42)at com.example.service.OrderService.process(OrderService.java:88)at com.example.service.OrderService.process(OrderService.java:88)← 同一行重复出现,大概率是 while(true) 或 for(;;) 未设退出条件
第四步:交叉验证,排除干扰
有时 jstack 看似在循环,实则是 JVM 正在 GC 或线程挂起。可快速做两件事验证:
- 再执行一次
top -H -p 12345,观察该 TID 的 CPU 占比是否持续稳定在高位(比如连续 3 次都 >95%),而非忽高忽低 - 用
perf record -p 12345 -g -- sleep 5 && perf script | head -20查看真实采样热点,若大量命中你的业务类方法(如OrderService.process),基本坐实死循环
不复杂但容易忽略。


















