Skip to content

线上业务服务器 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 日志)再重启,否则问题消失无从查起。