CPU 占用突然飙升是线上环境最常见的问题之一。排查这类问题的基本思路是层层缩小范围:先确定哪个进程在消耗 CPU,再找到进程内哪个线程、哪段代码造成的,最后分析根本原因并解决。整个过程可以用几条常用命令串联起来。
1. 快速确认现象
当你收到告警说服务器 CPU 使用率过高,首先登录服务器,用 top 或 htop 观察实时状态。
top
在 top 界面里,重点关注 %Cpu(s) 这一行的 us(用户态)、sy(内核态)、id(空闲)等指标。如果 us 或 sy 长期高于平常水平,说明确实存在性能问题。按 P 键可以让进程按 CPU 占用率排序,最耗 CPU 的进程会排在最前面。
2. 定位高 CPU 占用的进程
在 top 或 htop 中,直接可以看到哪几个进程的 %CPU 数值异常高,记下它们的 PID。如果想用一行命令快速输出,可以用 ps:
ps -eo pid,ppid,cmd,%cpu --sort=-%cpu | head
这会列出 CPU 占用最高的 10 个进程。有时候问题进程可能是某个 Docker 容器、Java 应用或数据库服务,PID 就是我们进一步分析的关键入口。
3. 定位高 CPU 占用的线程
当一个进程内包含多个线程(例如 Java、Go、Python 的多线程程序),还需要确定具体是哪个线程在消耗 CPU。top 可以打开线程视图:
top -H -p <PID>
-H 开启线程显示,现在每个线程都有独立的 CPU 占用率行。记下 %CPU 最高的线程 ID(TID)。对于 Java 应用,可以用 jstack 等工具进一步获取线程堆栈,但前提是知道该线程在操作系统层面的 TID。由于 Java 线程栈里记录的是 16 进制的 nid,需要将 TID 转换为 16 进制:
printf "%x\n" <TID>
然后到 jstack <PID> 的输出中搜索这个十六进制值,就能定位到具体的 Java 线程名和代码调用栈。
对于非 Java 程序,可以使用 perf 或 strace 进一步定位热点。
4. 定位占用 CPU 的具体代码
找到问题线程后,我们需要知道它正在执行什么代码,是哪些函数或指令导致了高 CPU。最直接的方法是利用 perf top 实时采集系统或指定进程的 CPU 热点函数:
perf top -p <PID>
这个命令会动态刷新显示占用 CPU 时钟最多的函数。如果是 C/C++ 程序,可以看到用户态函数名,如果是 Python 或 Java,可能需要额外符号解析,或者结合语言专用的 profiling 工具(如 py-spy、async-profiler)。
另一种场景是程序陷入死循环,或频繁进行系统调用。此时可以用 strace 跟踪进程的系统调用:
strace -c -p <PID>
-c 会在退出时统计各个系统调用的次数和耗时,如果某个系统调用(如 futex、poll、read)次数异常多,那往往是锁竞争或错误轮询引起的高 CPU。也可以直接实时查看系统调用序列:
strace -p <PID>
不过要小心 strace 本身会带来额外的性能开销,线上环境需谨慎使用,尽量在测试环境复现时使用。
生成火焰图 是另一种直观呈现 CPU 热点的强大方法。使用 perf 录制一段时间的 CPU 采样数据,然后用 Brendan Gregg 的 FlameGraph 脚本生成火焰图:
perf record -p <PID> -g -- sleep 30
perf script > out.perf
./stackcollapse-perf.pl out.perf > out.folded
./flamegraph.pl out.folded > flamegraph.svg
在火焰图中,横轴宽度代表该函数栈在采样中出现的比例,可以一目了然地看到哪条调用路径消耗了最多的 CPU。
5. 分析根本原因
定位到具体代码后,需要结合业务逻辑和系统状态分析原因。常见典型原因包括:
- 无限循环或低效算法:代码中某个 while 循环没有正确的退出条件,或在大数据集上使用了 O(n²) 复杂度的算法。此时应检查相关代码逻辑。
- 频繁的 GC 或内存分配:Java、Go 等语言的垃圾回收若过于频繁,也会消耗大量 CPU。通过
jstat -gc <PID> 1000或 Go 的pprof查看 GC 指标。 - 锁竞争:多线程程序里,某个锁被大量线程争抢,导致多数线程不断自旋(spin)或在内核中忙等。
perf lock或应用层内置的锁分析工具可以确认。 - 过多的系统调用:比如在循环中每次都调用
time()或read()小量数据,导致上下文切换频繁。strace -c可以暴露这类问题。 - 中断或内核热点:如果
top显示sy很高,说明 CPU 消耗在内核态。可能是网卡中断分配不均衡、大量短连接导致softirq高负载等。可以用mpstat、sar和perf进一步分析。
6. 常用工具速查表
| 工具 | 用途 |
|------|------|
| top / htop | 系统总览,发现高 CPU 进程 |
| ps | 命令行打印进程 CPU 占用率 |
| top -H | 查看进程中各线程的 CPU 占用 |
| perf top | 实时显示 CPU 热点函数 |
| perf record & report | 采样并生成 CPU 调用栈报告 |
| strace -p | 跟踪系统调用 (小心性能影响) |
| pidstat -u 1 | 周期性输出进程 CPU 使用率 |
| jstack (Java) | 导出 Java 线程堆栈,结合 TID 定位 |
掌握这条“进程 → 线程 → 代码 → 原因”的排查思路,再配合上述几个简单命令,绝大多数 CPU 飙高的问题都能在短时间内找到症结。切记在线上先别急于深入使用高开销工具,优先用 top、pidstat 等低影响的方式收集信息,确认范围后再进行针对性的详细分析。