线上服务器CPU飙升 Java代码 完整定位排查流程(生产实战+面试标准答案)
线上CPU飙升是后端最高频故障,核心思路:服务器定位高CPU进程 → 定位高CPU线程 → 打印线程栈 → 锁定问题代码 → 分析根因。整套流程3分钟内可定位代码级问题。
一、整体排查链路(口诀)
top 找进程 → top -H 找线程 → printf 转16进制 → jstack 抓栈 → 定位代码行
二、第一步:服务器层找到高CPU Java进程
服务器执行:
top观察:CPU占用最高的 PID(一般是java进程)。
假设高CPU进程PID = 12345
三、第二步:找到该进程下的【高CPU线程】
进入进程线程视图:
top -H -p 12345-H:展示线程级别CPU占用
找到占用CPU最高的 线程TID,例如:67890
四、第三步:线程ID转16进制(jstack必备)
jstack 日志中线程ID是16进制,需要转换:
printf "%x\n" 67890得到十六进制线程ID:10812
五、第四步:jstack 打印堆栈,精准定位代码行
jstack 12345 | grep -A 50 10812-A 50:打印该线程后50行堆栈,直接看到类名、方法、代码行号
此时可以直接锁定:哪一行代码死循环、频繁计算、卡死
六、补充:全程一键排查命令(生产神器)
线上可直接复制执行,自动抓取最高CPU线程堆栈:
pid=$(top -bn1 | grep java | awk '{print $1}' | head -1)
tid=$(top -Hbn1 -p $pid | awk '{print $1}' | head -2 | tail -1)
hex_tid=$(printf "%x\n" $tid)
jstack $pid | grep -A 100 $hex_tid七、CPU飙升常见Java代码根因(99%场景)
1. 死循环 / 空循环(最常见)
while(true)、for循环无退出条件、递归死递归
现象:单个线程CPU 100%,线程栈固定卡在某行代码
2. 复杂逻辑疯狂计算、大数据遍历
超大List/Map遍历、嵌套循环、笛卡尔积、复杂排序
数据量小时没问题,线上数据量大直接CPU打满
3. 正则表达式回溯卡死
贪婪正则、复杂匹配导致 正则回溯爆炸,CPU飙升
4. 频繁FullGC / GC线程占满CPU
不一定是业务代码!GC线程CPU高也是CPU飙高主力场景
排查命令:
jstat -gc 12345 1000频繁 FGCT 上涨 → 内存溢出、内存泄漏、大对象创建
此时需要配合 jmap dump 分析:
jmap -dump:format=b,file=heap.hprof 123455. 锁自旋、CAS空转
大量竞争锁、Unsafe CAS空转、synchronized 严重竞争导致线程自旋消耗CPU
6. 定时任务频繁执行、死锁循环任务
quartz、xxl-job 任务逻辑死循环、每秒高频执行
八、如何区分:业务线程CPU高 VS GC线程CPU高
- 业务线程高:jstack 能看到业务代码堆栈 → 业务逻辑问题
- GC线程高:jstack 看不到业务栈,GC任务爆满 → 内存问题(泄漏/大对象/频繁创建销毁)
九、临时止血方案(线上紧急处理)
- 临时重启服务(快速恢复可用性)
- 临时关闭异常定时任务
- 限流、降级防止流量打垮服务
- dump 堆栈+内存文件,保留现场后重启
十、面试极简背诵版(一句话流程)
线上CPU飙升,我先通过top找到高CPU进程PID,再用top -H定位高CPU线程TID,转16进制后通过jstack抓取该线程堆栈,直接定位到具体类和代码行;同时用jstat排查是否是GC导致的CPU飙高,区分业务死循环还是内存问题。
十一、拓展:日常预防手段
- 线上开启线程池监控、GC监控、CPU告警
- 禁止裸 while(true) 循环,增加休眠和退出条件
- 大集合遍历、复杂计算做数据量限制
- 定时任务错开高峰、控制执行频率