Skip to content

线上 CPU Load 飙高如何排查? ​

  • 标签:后端、场景题

🏛 业务架构师 ​

CPU Load 飙高的排查我有固定套路,但第一步永远是看业务影响:核心接口有没有报错/超时?有资损就先按应急预案止损(扩容、降级、必要时保留现场后重启),定位可以放在止损之后——先恢复生意再查病因,这个优先级不能反。

定位四步法

bash
top                 # 1. 确认进程
top -Hp <pid>       # 2. 找到吃CPU的线程 tid
printf '%x' <tid>   # 3. 线程号转16进制
jstack <pid> | grep -A 20 <tid-hex>  # 4. 定位到代码行

常见根因(按我实际处理频率排序)

  • 频繁 GC:jstat -gcutil 看,Full GC 风暴把 CPU 烧在内存回收上,根因是内存问题不是 CPU 问题——大对象、缓存无限增长、堆配置不合理
  • 死循环/正则回溯:业务代码 bug,日志里循环刷屏
  • 序列化/大报文:JSON 转换大对象、日志同步刷盘
  • 线程数失控:没设上限的 new Thread / 每请求建线程,上下文切换把 load 顶起来(load 高但 CPU% 不高是典型信号)
  • 流量突增:真·容量不足,看 QPS 曲线是否同步上涨,是营销活动还是被刷(那就先切限流)

业务视角收尾:load 高只是症状,要区分"业务涨了"还是"代码坏了"——前者走扩容和容量复盘(下次大促提前压测),后者走代码修复。我要求团队对核心服务做容量基线管理:日常水位、极限水位、扩容触发线三条线画清楚,事故时才有决策依据,不用拍脑袋。

🏗 系统架构师 ​

CPU load 飙高我按"先定位类型,再定位线程,最后定位根因"三步,全程不重启服务。

第一步:top 看清是哪种高

bash
top -Hp <pid>        # 找出 CPU 最高的线程 tid
printf '%x\n' <tid>  # 转十六进制准备对 jstack
jstack <pid> | grep -A 20 nid=0x<hex>

同时看 top 的 us/sy/wa 比例,不同比例结论完全不同:

  • us 高:业务代码或 GC。GC 场景 jstack 里会是大量 GC 线程,配合 jstat -gcutil 看 Full GC 频率。
  • sy 高:系统调用/上下文切换风暴,常见于频繁创建销毁线程、锁自旋。vmstat 1 看 cs 列,pidstat -wt 找切换大户。
  • wa 高:CPU 在等 IO,本质是磁盘瓶颈或慢 SQL 拖的,去查 DB 和磁盘 util,CPU 只是受害者。

第二步:常见根因清单

  • 流量突增或重试风暴(上游超时引发放大)。
  • 死循环/正则回溯失控/大集合遍历,jstack 里同一栈反复出现。
  • Full GC 循环:堆内存泄漏,老年代打满,CPU 被 GC 线程吃光——线上 load 高 50% 以上是 GC 问题。
  • 序列化/加密等 CPU 密集调用被刷(被恶意流量打)。

第三步:止血与根治 止血:限流降级非核心接口、摘流量、扩容。根治:按根因修代码/调内存参数(-Xmx 与容器 limit 对齐)/加缓存。

架构师视角的收尾:load 告警要配梯度(如 5 核机器 load > 5 告警、> 10 自动扩容),把排查 SOP 固化成 runbook 而不是靠临场发挥。

💻 全栈开发专家 ​

我按固定套路排查,五分钟内基本能锁定元凶。

排查步骤

  1. top 先看是 us 高(用户态,代码问题)还是 sy 高(系统调用/上下文切换)、wa 高(IO 等待)。
  2. top -Hp <pid> 找出最耗 CPU 的线程 TID。
  3. printf '%x\n' <tid> 转十六进制,去 jstack <pid> | grep -A 20 <hex> 定位栈。线上我直接用 Arthas 一条龙:thread -n 3 直接打印最忙线程栈,还免了转码。

常见元凶

  • 死循环 / while 条件写错 / 正则回溯灾难
  • 频繁 Young GC:jstat -gcutil <pid> 1000 看 FGC/YGC 曲线,GC 线程会吃满 CPU,配合 dump 分析大对象
  • 序列化(JSON toString)、大量字符串拼接、大集合排序
  • sy 高通常是线程数过多导致上下文切换(vmstat 看 cs),典型是线程池滥用

2026 年补充一点 虚拟线程项目里 CPU 高要留意:pin 到载体线程导致的 carrier 拥堵、或虚拟线程里跑了 CPU 密集任务,jcmd <pid> Thread.dump_to_file -format=json 能看到虚拟线程视角的栈,普通 jstack 只能看到载体线程,别被误导。

善后 SkyWalking 里同步看这类接口的 RT 曲线,确认是哪个入口引发,再决定扩容还是改代码。