当 CPU 使用率突然飙升至 100% 时,需快速定位原因并解决。以下是 分步排查方法 和 应急处理方案,适用于 Linux/Unix 系统:
一、快速定位 CPU 瓶颈
1. 定位高 CPU 进程
# 实时查看进程 CPU 占用(按 %CPU 排序)
top
# 或使用更直观的 htop(需安装)
htop
# 快速过滤高 CPU 进程(示例:显示前5个)
ps -eo pid,ppid,cmd,%cpu,%mem --sort=-%cpu | head -n 6
2. 分析进程内的线程
# 查看指定进程的线程 CPU 占用(替换 PID)
top -H -p <PID>
# 或使用 ps 按线程查看
ps -T -p <PID> -o tid,pcpu,cmd
3. 生成线程堆栈(Java 应用专用)
# 生成线程转储(需 JDK)
jstack <PID> > jstack.log
# 结合 top 找到的高 CPU 线程 ID(转成16进制)
printf "%x\n" <TID> # 输出如 1b3e
# 在 jstack.log 中搜索 "nid=0x1b3e" 定位线程代码
二、常见原因及应急处理
1. 代码死循环或低效算法
-
现象:某个线程长期占用 CPU。
-
解决:
- 通过
jstack或gdb分析线程堆栈,定位代码位置。 - 重启服务或关闭问题接口(临时方案)。
- 优化算法(如避免嵌套循环、减少递归深度)。
- 通过
2. 频繁 GC(Java 应用)
-
现象:GC 线程(如
Gang worker#0)占用高 CPU。 -
验证:
# 查看 GC 统计 jstat -gcutil <PID> 1000 10 -
解决:
- 临时增大堆内存:
-Xmx4g -Xms4g。 - 优化代码减少对象创建。
- 临时增大堆内存:
3. 锁竞争或死锁
-
现象:线程状态为
BLOCKED或WAITING。 -
验证:通过
jstack检查锁持有情况。 -
解决:
- 优化锁粒度(如用读写锁替代独占锁)。
- 重启服务临时恢复。
4. 外部攻击或异常流量
-
现象:大量网络连接(如
ESTABLISHED状态)。 -
验证:
# 查看 TCP 连接数 netstat -nat | awk '{print $6}' | sort | uniq -c # 或检查访问日志 tail -f /var/log/nginx/access.log -
解决:
- 防火墙限流:
iptables或云平台安全组。 - 使用 CDN 或 WAF 过滤恶意请求。
- 防火墙限流:
三、系统级排查工具
1. 系统负载监控
# 查看过去1/5/15分钟平均负载(应小于CPU核心数)
uptime
# 统计 CPU 上下文切换和中断
vmstat 1 5
2. 性能分析(Perf)
# 安装 perf
apt-get install linux-tools-common
# 采样 CPU 热点函数(采样10秒)
perf record -F 99 -a -g -- sleep 10
perf report
3. 火焰图生成
# 下载 FlameGraph 工具
git clone https://github.com/brendangregg/FlameGraph.git
# 生成火焰图(需 perf 数据)
perf script | ./FlameGraph/stackcollapse-perf.pl | ./FlameGraph/flamegraph.pl > flame.svg
四、临时缓解措施
-
限流降级:关闭非核心功能,减少 CPU 消耗。
-
重启服务:强制释放资源(可能丢失会话数据)。
-
调整优先级:降低问题进程的 CPU 优先级。
renice -n 19 -p <PID> # 优先级调整为最低(19)
五、预防与优化
- 监控告警:配置 Prometheus + Grafana 监控 CPU 和线程状态。
- 压测优化:定期进行压力测试,识别性能瓶颈。
- 代码审查:避免无限循环、未优化的正则表达式等。
- 资源隔离:使用容器(Docker)或 cgroups 限制进程资源。
案例:Java 应用正则表达式导致 CPU 100%
- 现象:某线程持续运行
java.util.regex.Pattern。 - 排查:
jstack发现线程在执行Matcher.find()。 - 根因:低效的正则表达式(如贪婪匹配)。
- 解决:优化正则表达式,或改用字符串解析。
通过以上步骤,可快速定位并解决 CPU 飙升问题。建议结合监控系统提前预防,并保留排查记录供后续分析。