CPU负载诊断起点:top与load average解读
Linux服务器CPU负载异常时,top命令是最直接的诊断入口。执行top后,头部区域显示系统级负载指标,其中load average三个数值分别代表1分钟、5分钟、15分钟的平均负载:
$ top
top - 14:32:01 up 42 days, 3:21, 3 users, load average: 8.52, 6.31, 4.18
Tasks: 287 total, 2 running, 285 sleeping
%Cpu(s): 82.3 us, 8.1 sy, 0.0 ni, 9.2 id, 0.1 wa, 0.0 hi, 0.3 si
load average与CPU核心数的关系:单核CPU的load average超过1.0表示有进程在排队等待;8核服务器load average达到8.0时CPU资源基本满载。判断负载是否异常,用nproc获取核心数作为基准:
$ nproc
16
16核服务器load average达到8.52属于中度偏高,但尚未满载。如果load average持续超过核心数的1.5倍,说明CPU资源严重不足。
%Cpu(s)行需要重点关注:us高表示用户态进程消耗CPU,通常是业务程序;sy高表示内核态开销大,可能与系统调用、上下文切换或中断处理有关;wa高表示I/O等待,CPU负载高但实际计算不密集,瓶颈在磁盘或网络I/O。
进程级CPU占用分析:top字段与ps辅助定位
top默认按CPU占用排序,按P键可手动按CPU排序。关键字段包括%CPU(进程CPU占用百分比)、TIME+(累计CPU时间)和COMMAND(进程名):
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8521 appuser 20 0 2.5g 1.2g 12m S 98.7 3.2 12:45.33 java
8535 appuser 20 0 850m 320m 8m S 45.2 0.8 5:12.18 node
8520 mysql 20 0 1.8g 850m 15m S 32.1 2.1 8:33.45 mysqld
单个进程%CPU接近100%说明该进程占满了一个核心。多线程进程可能超过100%(如160%表示占用了1.6个核心)。
ps命令提供更灵活的进程筛选,配合--sort参数按CPU排序:
$ ps aux --sort=-%cpu | head -15
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
appuser 8521 98.7 3.2 2621440 1258292 ? Ssl 14:01 12:45 java -jar app.jar
appuser 8535 45.2 0.8 870400 327680 ? Ssl 14:03 5:12 node server.js
定位到具体进程后,top -p PID -H可以查看进程内线程级CPU占用,-H参数显示线程:
$ top -p 8521 -H
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
8530 appuser 20 0 2.5g 1.2g 12m R 88.5 3.2 8:12.33 java
8531 appuser 20 0 2.5g 1.2g 12m R 10.2 3.2 1:33.11 java
线程ID 8530占了88.5% CPU,用printf "%x\n" 8530转为十六进制(0x2152),再配合jstack(Java)或pstack(C/C++)查看该线程的调用栈。
perf采样分析:定位内核态与用户态热点函数
top只能看到进程级占用,perf工具可以采样CPU性能计数器,定位到函数级别的热点。perf top实时显示CPU热点函数:
$ sudo perf top
Samples: 40K of event 'cpu-clock', 4000 Hz
12.34% java java [.] Compiler::compile_method
8.21% java java [.] GCTaskThread::run
5.67% [kernel] [k] __do_softirq
3.45% java java [.] StringBuilder.append
perf record采集数据后用perf report离线分析:
# 采集10秒,指定进程
$ sudo perf record -p 8521 -g -- sleep 10
[ perf record: Woken up 42 times to write data ]
[ perf record: Captured and wrote 10.234 MB perf.data ]
# 查看报告
$ sudo perf report --stdio
-g参数记录调用栈,perf report中以调用图形式展示函数开销。输出中[.]表示用户态函数,[k]表示内核态函数。用户态函数占比高,优化方向在业务代码;内核态函数占比高,需要排查系统调用频率、锁竞争或中断处理。
火焰图生成与可视化分析:FlameGraph工具链
perf输出文本报告不够直观,火焰图将采样数据可视化为调用栈层次图,由Brendan Gregg开发的FlameGraph工具生成:
# 1. perf采集调用栈数据
$ sudo perf record -F 99 -p 8521 -g -- sleep 30
# 2. 将perf数据转为文本格式
$ sudo perf script > out.perf
# 3. 折叠调用栈
$ git clone https://github.com/brendangregg/FlameGraph.git
$ ./FlameGraph/stackcollapse-perf.pl out.perf > out.folded
# 4. 生成火焰图SVG
$ ./FlameGraph/flamegraph.pl out.folded > cpu_flamegraph.svg
火焰图中,横轴是CPU时间占比,纵轴是调用栈深度。最宽的函数块是CPU热点。Java应用可以用async-profiler替代perf,直接生成火焰图且开销更低:
$ ./async-profiler/profiler.sh -d 30 -f cpu.html 8521
分析火焰图时关注两点:顶层最宽的方块是CPU直接消耗点;中间层异常宽的方块可能是循环或递归调用。常见的高CPU模式包括:GC频繁(GCTaskThread占比超过15%)、正则表达式回溯(Pattern.match)、序列化反序列化热点(Jackson或Gson方法)、锁自旋等待。
常见高CPU场景与处置方案
Java应用CPU飙升最常见的原因是GC频繁。用jstat -gcutil PID 1000 10每秒采样GC状态:
$ jstat -gcutil 8521 1000 10
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 98.44 55.33 89.12 94.20 91.30 142 3.812 18 5.234 9.046
0.00 98.44 61.20 92.45 94.20 91.30 143 3.845 19 5.567 9.412
0.00 98.44 72.15 96.78 94.20 91.30 145 3.901 21 6.012 9.913
Full GC次数(FGC)持续增长且耗时增加,Old区(O)接近90%,说明内存不足导致频繁GC。处置方向:增大堆内存或排查内存泄漏。
非Java应用的CPU问题,常见场景及处置:
死循环:火焰图表现为单一函数块异常宽,代码中存在未设置退出条件的循环。无限递归:调用栈深度异常,配合ulimit -s查看栈大小。锁竞争:perf中futex系统调用占比高,strace -c -p PID可统计系统调用频率。线程数过多:ps -eLf | wc -l查看总线程数,上下文切换开销通过vmstat 1的cs列判断,超过10万次/秒说明上下文切换严重。
定位CPU问题的工具链:top看全局负载,top -H -p看线程级占用,perf采样热点函数,火焰图可视化调用栈,代码定位修复。这套流程覆盖了从宏观负载到微观函数的完整排查路径。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/linux-fu-wu-qi-cpu-fu-zai-biao-sheng-pai-cha-shi-zhan-top/