Linux 上排查 CPU 占用高的五个步骤
以前遇到 CPU 高就是 top 看一眼,看到某个进程 300% 就重启了事。
后来才明白,重启只是把问题推迟。真正该先搞清楚的是:这个 CPU 是在干活,还是在等东西。
第一步:先看全局,别急着看进程
top
只看顶部那几行就够,重点在 %Cpu(s) 这一行:
us高 —— 用户态在算,通常是自己代码的问题sy高 —— 内核态,看系统调用、上下文切换、网络收发wa高 —— 在等磁盘,这不是 CPU 忙,是 IO 慢si/hi高 —— 软硬中断,常见于网卡压力大st高 —— 被宿主机其他虚拟机抢了,云主机上很常见
如果
wa高而us不高,去查磁盘和内存,别去翻代码。这一步判断错,后面全白做。
第二步:定位到进程
top -o %CPU
# 或按内存排序,有时是 GC 引起的高 CPU
top -o %MEM
记下 PID。同时留意 RES(常驻内存)——如果内存也一直涨,多半是内存压力导致频繁回收,CPU 高只是结果。
第三步:定位到线程
多线程程序里,进程总共 300% 不代表某个线程有问题。
top -H -p <PID>
# 或者
ps -T -p <PID> -o spid,pcpu,comm --sort=-pcpu | head
Java 程序可以把线程 ID 转十六进制,去 jstack 输出里找 nid=0x... 对应的栈;
C/C++ 程序直接用 gdb -p <PID> 加 thread apply all bt。
第四步:看它到底在调什么
有 perf 就用 perf,没有就先装:
# 采样 10 秒,看热函数
perf top -p <PID>
# 或者抓一段时间的调用栈
perf record -F 99 -p <PID> -g -- sleep 15
perf report
没有 perf 权限时,用 pidstat 看上下文切换也很有效:
pidstat -w -t -p <PID> 1 5
cswch/s(自愿切换)很高,说明在等锁或等 IO;
nvcswch/s(非自愿)很高,说明时间片被抢,通常是线程开太多。
第五步:对着现象下结论
把前面几步的结果对上号,基本就定方向了:
| 现象 | 大概率原因 |
|---|---|
| us 高,单个线程 100% | 死循环、正则回溯、大对象序列化 |
| sy 高,cswch/s 高 | 锁竞争、线程过多、频繁小包读写 |
| wa 高 | 慢盘、swap 抖动、大文件顺序读 |
| st 高 | 宿主机超卖,只能换机器或错峰 |
两个我踩过的坑
坑一:只看平均负载
uptime 里的 load average 包含不可中断睡眠的进程,所以磁盘卡住时 load 会飙升,
但 CPU 可能很闲。拿 load 当 CPU 指标会误判。
坑二:忘了看容器限制
在容器里 top 看到的是宿主机的 CPU 数和总占用,容易以为还有余量。
先确认自己的配额:
cat /sys/fs/cgroup/cpu.max # cgroup v2
cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us # cgroup v1
配额打满时进程会被 throttle,表现是「明明有 8 核但跑不快」,这时候再优化代码意义不大。
顺序不能跳:全局 → 进程 → 线程 → 系统调用 → 结论。 跳过任何一步,都会得到「CPU 很高,不知道为啥」这种结论。