Linux 服务器进程占用过高如何定位
“进程占用过高”通常指以下几种情况之一(按严重程度排序):
- 某个进程 CPU 使用率持续很高(单核/多核接近 100%)
- 某个进程内存占用(RSS / VSZ)持续线性增长或占比异常大
- 某个进程打开的文件描述符(fd)数量爆炸
- 某个进程产生大量磁盘 IO、线程数过多、上下文切换爆炸等间接导致系统整体变慢
以下是目前最常用、最有效的一套分层定位流程,从 30 秒粗筛到深度分析,通常 5–30 分钟内可以锁定主要嫌疑进程和原因类型。
第一步:30 秒粗筛
| 顺序 | 命令 | 看什么(异常阈值参考) | 指向的方向 |
|---|---|---|---|
| 1 | top 或 htop | 按 P 看 %CPU,按 M 看 %MEM,按 1 看多核均衡性 | CPU / 内存 Top 进程 |
| 2 | ps -eo pid,ppid,%cpu,%mem,rss,vsz,comm –sort=-%cpu | head -15 | %cpu、rss(驻留内存 KB)、vsz(虚拟内存 KB) | CPU / 内存 Top 进程(更精确排序) |
| 3 | top → 按 t 切换到线程视图 | 看是否某个进程下多个线程同时吃 CPU | 多线程程序哪个线程在消耗 |
| 4 | mpstat 1 5 或 mpstat -P ALL 1 5 | 每个核 %usr + %sys 是否均衡?是否有单核 100% | 单核热点 vs 多核均衡负载 |
一句话判断模板(跑完这几条后写出来):
“8 核机器,top 显示 java 进程 %CPU 780%(接近 8 核满载),rss 12.8G,mpstat 看到 cpu4–7 持续 95–100%,其他核 10–30% → 疑似 java 进程计算密集 + 线程绑定不均”
第二步:按占用类型分类定位
| 占用类型 | 典型 top/htop 画面特征 | 关键指标阈值参考 | 下一秒最有用命令 | 常见罪魁祸首 |
|---|---|---|---|---|
| CPU 用户态密集 | %CPU 接近 100% × 核数,us 很高,单个/少量进程 | 单进程 %CPU > 90–100% × 核数 | pidstat -u 1 5 | sort -k8 -nr perf top | 死循环、正则匹配、加密/压缩、数值计算 |
| CPU 内核态密集 | sy 很高(>30–60%),us 不高 | sy > 30–50%,softirq 高 | mpstat 1 5 cat /proc/softirqs | 高 PPS 网络包、iptables 规则多、ksoftirqd |
| 内存占用爆炸 | RSS / %MEM 持续线性增长或占比 > 50–80% | available 下降,swap 开始使用 | ps -eo pid,rss,vsz,cmd –sort=-rss | head smem -t -k | 内存泄漏、大对象缓存、Java 堆外内存 |
| 线程数爆炸 | 进程 %CPU 不高,但 NLWP(线程数)几千上万 | ps -eLf | grep PID | wc -l > 1000–5000 | ps -eLf | awk ‘{print $2}’ | sort | uniq -c | sort -nr | 线程池不回收、频繁创建线程、连接池耗尽 |
| 文件描述符爆炸 | lsof -p PID | wc -l 几千上万 | ulimit -n 限制被打满 | lsof -p PID | awk ‘{print $NF}’ | sort | uniq -c | sort -nr | socket 泄漏、未关闭文件、大量日志句柄 |
| 磁盘 IO 间接拉高负载 | wa/iowait 高,%util 高,但 CPU 不满 | iostat %util > 80–90%,await > 10–30ms | iotop -o pidstat -d 1 5 | 日志狂写、数据库 WAL/binlog 同步写 |
第三步:锁定热点函数 / 代码位置
| 场景 | 推荐工具与命令 | 输出价值 | 适用语言 / 场景 |
|---|---|---|---|
| 通用 CPU 热点 | perf top 或 perf record -F 99 -p PID -g — sleep 30 → 生成火焰图 | 火焰图直观显示热点函数、调用栈 | C/C++/Rust/Go/Java(需符号表) |
| Java 进程 | async-profiler(最推荐) ./profiler.sh -d 30 -f flame.html PID | 支持 JIT、锁、GC、线程状态火焰图 | Java / Kotlin / Scala |
| Python 进程 | py-spy top / py-spy record -p PID -o flame.svg –duration 30 | 采样火焰图,无需修改代码 | Python(包括 GIL 瓶颈) |
| Node.js | node –prof PID → node –prof-process isolate-*.log -o flame.svg | V8 优化器 / GC / 事件循环热点 | Node.js |
| 高上下文切换 / 锁等待 | perf record -e syscalls:sys_enter_futex … 或 bpftrace 追踪 futex/mutex | 显示锁等待时间分布、持有者 | 多线程程序、数据库连接池 |
火焰图解读小技巧:
- 宽块 = 占用 CPU 时间长
- 高块 = 调用栈深
- 平顶 = 叶子函数在吃 CPU(死循环、正则、加密等)
- 红色/橙色区域多 = 锁等待或 GC 暂停
第四步:临时缓解 + 根因闭环
临时止血常用手段(按风险从低到高):
- taskset -c 0-3,8-11 PID → 绑核减少争抢(慎用)
- renice -n -10 -p PID 或 chrt -r -p 20 PID → 提高优先级
- kill -STOP PID → 暂停观察系统是否恢复
- 重启进程 / 容器(最常见操作,但要确认无状态或有主从)
根因闭环检查清单:
- 是否业务高峰正常现象?
- 是否近期代码/配置/依赖升级引起?
- 是否资源超卖(云主机 steal 高)?
- 是否需要加机器 / 拆分服务 / 优化算法?
一句话核心思路:
先用 top / pidstat / mpstat 粗筛占用类型 → 再根据类型选 perf / async-profiler / py-spy 等工具生成火焰图 → 最后结合业务代码 / 配置 / 锁定位根因