线上 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 而不是靠临场发挥。
💻 全栈开发专家
我按固定套路排查,五分钟内基本能锁定元凶。
排查步骤
top先看是us高(用户态,代码问题)还是sy高(系统调用/上下文切换)、wa高(IO 等待)。top -Hp <pid>找出最耗 CPU 的线程 TID。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 曲线,确认是哪个入口引发,再决定扩容还是改代码。