应优先用pidstat -u 1 1,因其输出稳定、可分离%usr(用户态)和%sys(内核态)消耗,便于判断是应用逻辑问题还是系统调用异常,而top适合快速扫视但易受交互干扰。

直接杀掉占用最高的进程往往治标不治本,甚至可能中断关键服务;真正要解决 Linux CPU 100% 问题,得按「进程 → 线程 → 代码」三级穿透,否则容易反复。
怎么快速定位到高 CPU 进程(top 和 pidstat 选哪个)
top 适合第一眼扫视,但默认刷新干扰多、排序不稳定;pidstat -u 1 1 更干净,能直接分离用户态(%usr)和内核态(%sys)消耗——这对后续判断是代码问题还是系统调用问题很关键。
- 如果
%usr高(比如 >80%),大概率是应用逻辑问题:死循环、低效算法、频繁对象创建 - 如果
%sys高(比如 >30%),重点查锁竞争、上下文切换(pidstat -w 1看%ctxt)、或驱动/内核模块异常 - 别只看
%CPU总值,ps -eo pid,ppid,cmd,%cpu --sort=-%cpu | head -n 5能绕过 top 的交互干扰,输出更稳定
为什么 top -H -p PID 找到的线程 ID 要转成十六进制
因为 Java 的 jstack、Go 的 pprof、甚至内核级工具如 perf 在堆栈中显示的线程 ID 默认是十六进制(小写),而 top -H 显示的是十进制。不转换就 grep 不到对应线程的调用栈。
- 正确命令是:
printf "%x\n" <tid></tid>,不是echo "obase=16; <tid>" | bc</tid>(后者可能输出大写,jstack匹配失败) - 常见坑:线程 TID 和进程 PID 在 Linux 下共享命名空间,但
jstack输出里看到的是线程的tid字段,不是nid;确认时用jstack <pid> | grep -A 5 "nid=0x.*"</pid>对照 - 如果进程不是 Java,比如是 Node.js 或 Python,优先用
pstack <pid></pid>或gdb -p <pid> -ex "thread apply all bt" -ex quit</pid>,避免依赖语言特有工具
怎么判断是 CPU 真忙,还是在等 I/O(%iowait 到底怎么看)
iostat -c -x 1 输出里的 %iowait 并不表示“CPU 在等磁盘”,而是“CPU 本可运行但因无任务可做、且有 I/O 在进行中,所以空闲”。它高 ≠ 磁盘慢,反而可能是 CPU 已被其他任务占满,根本没机会去等 I/O。
- 真正要看的是:
%idle是否持续接近 0,同时%iowait却很低(比如 - 如果
%iowait > 20%且%idle也高(比如 >30%),才说明磁盘响应慢、CPU 被迫空转等待 - 混淆点:
vmstat 1中的wa列和iostat的%iowait含义一致,但top第一行的wa是采样统计,精度不如iostat
为什么 renice 和 kill -9 常常无效或危险
renice 只影响调度优先级,对已进入自旋锁、死循环或陷入内核不可中断状态(D 状态)的进程完全没用;kill -9 则可能让数据库丢事务、让服务端连接半关闭、或触发进程守护机制立刻拉起新实例,导致 CPU 再次飙升。
- 先用
ps -o pid,comm,state,wchan -p <pid></pid>看进程状态:R(运行中)可 kill,D(不可中断睡眠)基本只能重启 -
wchan列显示进程正在等待的内核函数名,比如wait_event_interruptible表示正常等待,do_wait可能是子进程僵死 - 真要终止,优先用
kill -15(SIGTERM)给进程留出清理时间;kill -9是最后手段,且执行后必须检查日志确认是否留下脏数据
最易被忽略的一点:CPU 100% 很少由单一线程引起,往往是多个线程高频争抢同一把锁(如 HashMap 并发写)、或 GC 线程与业务线程反复同步——这时候光看 top 排名前几的线程,会漏掉真正拖垮系统的“协同效应”。必须结合 pidstat -t -p <pid> 1</pid> 观察线程级波动,再用 perf record -p <pid> -g -- sleep 10</pid> 抓取火焰图,才能看清调用链路中的真实瓶颈。











