线上业务服务器 CPU 飙高如何排查?
- 标签:后端、线上问题排查
🏛 业务架构师
CPU 飘高的排查我有标准 SOP,目标 15 分钟内定位到代码行:
第一步:确认是谁(进程级)
top找出 CPU 高的 Java 进程;是业务线程还是 GC 线程,top -Hp <pid>看线程分布——如果最热的是 "GC task thread"/"G1 Young RemSet",问题不是业务代码是内存(GC 颠簸:堆不足或泄漏),转内存排查路线(jstat 看 GC 频率);- us 高 vs sy 高:用户态高=业务计算(代码问题);内核态高=系统调用/线程切换(IO 模型问题)。
第二步:定位到线程(线程级)
printf '%x' <tid>转十六进制,jstack <pid> | grep -A 20 <hex>找到线程栈——直接看到正在执行的代码行;- 生产环境我直接用 Arthas:
thread -n 5一条命令列出最耗 CPU 的 5 个线程及栈——排障效率的差距就在工具化程度。
第三步:按高频根因对号(业务级)
- 死循环/正则回溯:while 条件永真、灾难回溯的正则(嵌套量词)——正则没有压测过就上线是常见事故源;
- 大集合流操作:一次 filter/sort 百万元素、频繁正则编译(Pattern 应预编译复用);
- 序列化/反序列化风暴:大 JSON 每请求反复解析——该上缓存;
- GC 颠簸(第一步已分流):内存问题,修缓存与对象分配;
- 频繁 Young GC + 大量临时对象:循环内构造大对象;
- 锁自旋:synchronized 高竞争(jstack 看大量 BLOCKED)。
第四步:机制化预防:CPU 水位告警(70%)+ 自动抓栈(告警触发时自动保存 jstack/top 快照——问题往往是阵发性的,人工登机时已恢复,自动化留现场是关键)。
一句话:CPU 排查 = 进程→线程→栈→代码行的漏斗,GC 与业务分流是第一岔路口,Arthas 与自动留痕把 MTTR 从小时压到分钟。
🏗 系统架构师
线上 CPU 飙高我按标准的"定位进程 → 定位线程 → 定位代码"三步法:
1. 定位进程与 CPU 类型:
top找高 CPU 的 Java 进程;注意区分 us(用户态:业务代码/GC) 与 sy(内核态:系统调用/线程切换/锁自旋)、si(软中断:网络);- 若是多进程混部,先
pidstat -u确认。
2. 定位线程:
top -Hp <pid>找最高 CPU 的线程 TID,printf '%x'转十六进制;jstack <pid> | grep -A 20 <hex_tid>看该线程栈——一步锁定代码行;- 顺手看整体:GC 线程占比高 →
jstat -gcutil <pid> 1000看是不是 Full GC 风暴(CPU 飙高的头号常见原因:内存泄漏/缓存打满导致 GC 疯狂回收); - 线程数异常(几万线程在自旋/切换)→ sy 高,看锁竞争或无界线程池。
3. 常见根因清单(按概率排序):
- Full GC / 频繁 Young GC(内存问题);
- 死循环 / 正则回溯灾难 / 大集合遍历;
- 高并发下的锁自旋与上下文切换;
- 序列化反序列化、加解密等 CPU 密集任务突增;
- 机房流量激增(先看监控 QPS 是否同涨)。
4. 留现场再动手:jstack ×3(间隔抓多次看是否卡同一处)、jmap dump(内存嫌疑时)、arthas 一把梭(thread -n 3 直接列最忙线程栈 + dashboard)。
我的工具链:arthas(在线诊断首选)+ Grafana 看关联指标(QPS/RT/GC/线程数同屏对齐),根因往往在"变更"——先问最近发布了什么。
💻 全栈开发专家
四步定位法,一条命令链:
bash
# 1. 找到吃 CPU 的进程
top
# 假设 PID 12345,CPU 380%
# 2. 找到进程内吃 CPU 的线程
top -Hp 12345
# 假设线程 TID 12378 占 90%
# 3. 线程号转 16 进制
printf "%x\n" 12378 # 0x305a
# 4. 拿线程栈( nid=0x305a 定位)
jstack 12345 | grep -A 30 "nid=0x305a"或者直接 Arthas 一把梭:thread -n 5(自动列最忙线程栈),thread -b 找阻塞。
读栈定位类型:
- 业务死循环/大循环:栈顶是自己代码的 for/while——最常见;
- 正则回溯:栈顶在
java.util.regex.Pattern——恶意输入触发灾难性回溯; - 频繁 GC:栈顶在 GC 线程(
GC task thread/G1 Conc GC)——先用jstat -gcutil 12345 1000确认,病根通常是内存问题(泄漏/堆小),别误诊为 CPU 问题; - 序列化/加密:栈顶在 JSON/kryo/AES——大报文高频序列化,考虑换算法或缓存;
- 锁自旋:大量线程 BLOCKED + 个别线程长期持锁(
thread -b直接给答案)。
真实案例:一次 CPU 缓慢爬升到 200%,thread -n 3 发现全卡在 SimpleDateFormat.format——SimpleDateFormat 非线程安全,同学在循环里 new(不是复用导致错乱就是同步导致竞争),换 DateTimeFormatter + 静态常量后归零。
纪律:保留现场(jstack 连打三次间隔 5s、dump GC 日志)再重启,否则问题消失无从查起。