前言
服务器CPU突然飙到90%以上,告警响个不停。这时候需要快速定位是哪个进程、哪个线程、哪行代码在吃CPU。
这篇整理一套完整的排查流程,从定位进程到找出具体代码行,覆盖Java、Go、Python等常见语言。
一、先看是哪个进程
上去第一件事,top看一眼:
top输出里重点看这行:
%Cpu(s): 85.2 us, 3.1 sy, 0.0 ni, 10.5 id, 0.0 wa, 0.0 hi, 1.2 sius高说明是用户程序在吃CPU,wa高说明在等IO。如果us很高,基本就是业务代码的问题。
按P排序,罪魁祸首一目了然:
PID USER %CPU %MEM COMMAND 12345 app 156.0 8.2 java -jar app.jar好家伙,156%,跑满了一个半核心。
二、定位是哪个线程
Java进程里几百个线程呢,得继续缩小范围:
top-H -p12345-H显示线程级别。果然有个线程98%:
PID USER %CPU COMMAND 12367 app 98.0 java 12368 app 2.3 java记下这个线程ID:12367
三、定位具体代码
Java的话用jstack:
jstack12345>/tmp/thread.txt但是jstack里线程ID是16进制的,先转换一下:
printf"%x\n"12367# 304f然后搜:
grep-A30"nid=0x304f"/tmp/thread.txt输出:
"worker-1" #23 nid=0x304f runnable at java.util.regex.Pattern$GroupHead.match(Pattern.java:4658) at java.util.regex.Pattern$Loop.match(Pattern.java:4785) at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717) ...(省略一大堆regex相关的栈) at com.example.service.ContentFilter.filter(ContentFilter.java:89)破案了,正则在疯狂回溯。
四、常见原因与解决
CPU问题基本就那几种:
1)正则回溯
类似这种正则特别危险:
Stringregex="(a+)+b";// 遇到 "aaaaaaaaaaaaaaac" 直接卡死2)死循环
最傻的bug:
while(list.size()>0){process(list.get(0));// 忘了remove...}3)频繁Full GC
表现是好几个GC线程都在吃CPU。用这个看:
jstat -gcutil123451000FGC列如果一直在涨,就是内存泄漏了。
4)锁竞争
top里sy(系统态)高,jstack一看全是BLOCKED状态。
五、一键排查脚本
一个脚本,出问题直接跑,一键收集信息:
#!/bin/bash# cpu_debug.shPID=$1[-z"$PID"]&&echo"用法:$0<pid>"&&exit1DIR="/tmp/cpu_$(date+%H%M%S)"mkdir-p$DIRecho"收集进程$PID信息..."# 线程CPUps-T -p$PID-o tid,%cpu,time --sort=-%cpu|head-20>$DIR/threads.txt# jstackjstack$PID>$DIR/jstack.txt2>/dev/null# 找最忙的线程TOP_TID=$(head-2 $DIR/threads.txt|tail-1|awk'{print $1}')TOP_TID_HEX=$(printf"%x"$TOP_TID)echo"最忙线程:$TOP_TID(0x$TOP_TID_HEX)"grep-A30"nid=0x$TOP_TID_HEX"$DIR/jstack.txtecho"详细信息:$DIR"用法:./cpu_debug.sh 12345
直接告诉你哪个线程最忙,在执行什么代码。
六、Go程序的排查
Go比较方便,如果开了pprof:
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30进去之后top 10看热点函数,list 函数名看具体代码。
不想用命令行的话,加个-http=:8080参数,浏览器看火焰图,更直观。
七、预防措施
建议加个告警:
-alert:HighCPUexpr:100-(avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)>80for:3mCPU超过80%持续3分钟就告警。
另外代码里长时间运行的任务最好加个超时,别让一个bug把整个服务拖垮。
总结
CPU排查流程:
1. top/ps 确认哪个进程 ↓ 2. top -H 确认哪个线程 ↓ 3. jstack/pprof/perf 确认哪行代码 ↓ 4. 分析原因(死循环/GC/锁/正则...) ↓ 5. 修复并验证大多数情况下,问题出在:
- 业务代码Bug(死循环、正则回溯)
- 内存问题引发频繁GC
- 锁竞争严重
掌握这套流程,线上CPU问题基本都能快速定位。
有问题评论区交流。