当前位置: 首页 > news >正文

生产环境 cpu 飙高,如何排查

生产环境 cpu 飙高,如何排查

🎯 记住核心:ps -mp 找线程 → printf 转16进制 → jstack 看堆栈 → 定位代码!

一、标准排查流程(5步定位法)

第1步:确认是哪个进程占用CPU高(10秒)
# 方法1:查看所有进程CPU占用(按CPU降序)
ps aux --sort=-%cpu | head -10# 方法2:实时监控
top
# 按 Shift + P(按CPU排序)# 输出示例:
# USER   PID  %CPU %MEM    VSZ   RSS TTY  STAT START   TIME COMMAND
# app   12345 187.5 35.2 8234568 2345678 ?  Sl  10:23 125:34 java -jar myapp.jar

记录关键信息:

  • 进程PID:12345
  • CPU占用:187.5%(多核系统,单核100%,4核400%)
  • 进程命令:java -jar myapp.jar
第2步:找出该进程中CPU占用高的线程(30秒)

# 使用ps命令查看进程的所有线程,按CPU降序
ps -mp <PID> -o THREAD,tid,time,%cpu | sort -k4 -rn | head -10# 实际命令示例
ps -mp 12345 -o THREAD,tid,time,%cpu | sort -k4 -rn | head -10# 输出示例:
# USER     %CPU PRI SCNT WCHAN  USER SYSTEM   TID     TIME
# app      87.3  19    - -        -      -   23456  0:23:15  ← 热点线程
# app      45.2  19    - -        -      -   23457  0:18:42
# app       5.1  19    - -        -      -   23458  0:00:12

或使用top命令查看线程:


top -H -p 12345
# -H 显示线程
# -p 指定进程PID

记录高CPU的线程TID:

  • TID(线程ID):23456
  • CPU占用:87.3%
第3步:TID转换为16进制(5秒)

# jstack导出的堆栈中,线程nid是16进制格式,需要转换
printf "0x%x\n" <TID># 实际命令示例
printf "0x%x\n" 23456
# 输出:0x5ba0

记录16进制TID:0x5ba0

第4步:导出线程堆栈(1分钟)

# 导出jstack堆栈到文件
jstack <PID> > thread_dump_$(date +%Y%m%d_%H%M%S).txt# 实际命令示例
jstack 12345 > thread_dump_20250105_143022.txt# 建议连续导出3次(间隔3秒),对比分析
jstack 12345 > thread_dump_1.txt && sleep 3 && \
jstack 12345 > thread_dump_2.txt && sleep 3 && \
jstack 12345 > thread_dump_3.txt
第5步:搜索热点线程堆栈并定位代码(2分钟)

# 使用grep搜索对应的线程(用16进制nid)
grep -A 50 "nid=0x5ba0" thread_dump_1.txt# 输出示例:
"http-nio-8080-exec-23" #45 daemon prio=5 os_prio=0 tid=0x00007f8b3c012800 nid=0x5ba0 runnablejava.lang.Thread.State: RUNNABLEat com.example.service.OrderService.checkOrderStatus(OrderService.java:156)at com.example.service.OrderService.processOrder(OrderService.java:89)at com.example.controller.OrderController.createOrder(OrderController.java:45)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)...

关键信息提取:

  • 线程名:http-nio-8080-exec-23
  • 线程状态:RUNNABLE(正在运行)
  • 问题代码:OrderService.java:156
  • 方法名:checkOrderStatus()

二、快速判断问题类型

堆栈特征问题类型
RUNNABLE + 同一方法反复出现死循环
Pattern$Loop.match正则回溯
GC.task_thread频繁Full GC
BLOCKED + waiting to lock锁竞争
Socket/MySQL相关IO阻塞/慢SQL

三、常见问题案例与修复

案例1:死循环空转(CPU 100%)

堆栈特征:

at com.example.service.MessageConsumer.consume(MessageConsumer.java:45)
at com.example.service.MessageConsumer.consume(MessageConsumer.java:45)  // 重复
at com.example.service.MessageConsumer.consume(MessageConsumer.java:45)  // 重复

问题代码:

// ❌ 错误代码
while (true) {Message msg = queue.poll();  // 非阻塞,立即返回if (msg == null) {continue;  // 空转!CPU 100%}processMessage(msg);
}

修复方案:

// ✅ 正确代码
while (!Thread.interrupted()) {Message msg = queue.poll(100, TimeUnit.MILLISECONDS);  // 阻塞等待if (msg != null) {processMessage(msg);}
}
案例2:正则表达式灾难性回溯

堆栈特征:

at java.util.regex.Pattern$Loop.match(Pattern.java:4785)
at java.util.regex.Pattern$GroupTail.match(Pattern.java:4717)
at com.example.util.EmailValidator.validate(EmailValidator.java:23)

问题代码:

// ❌ 危险正则(嵌套量词)
Pattern pattern = Pattern.compile("^([a-zA-Z0-9]+)*@([a-zA-Z0-9]+)*\\.com$");
pattern.matcher("aaaaaaaaaaaaaaaaaaaaX").matches();  // 指数级回溯

修复方案:

// ✅ 简化正则
Pattern pattern = Pattern.compile("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$");// ✅ 加长度限制
if (email.length() > 100) {return false;
}
案例3:频繁Full GC导致CPU高

判断方法:

# 查看GC统计
jstat -gc <PID> 1000 10# 关键指标:
# FGC  - Full GC次数(快速增长)
# FGCT - Full GC总耗时(持续增加)
# OU   - Old区使用量
# OC   - Old区容量# 示例输出:
# S0C   S1C    S0U  S1U    EC      EU       OC       OU      FGC  FGCT
# 10240 10240  0    8192   81920   45000   204800   198000  89   156.7
#                                                    ^^^^^^  ^^   ^^^^^
#                                                    Old区97% Full GC 89次

修复方案:

  • 增大堆内存:-Xms8g -Xmx8g
  • 排查内存泄漏:jmap -histo:live | head -20
  • 优化代码:减少大对象创建、使用对象池
案例4:synchronized锁竞争

堆栈特征:

"http-nio-8080-exec-25" waiting for monitor entryjava.lang.Thread.State: BLOCKED (on object monitor)at com.example.cache.CacheService.get(CacheService.java:45)- waiting to lock <0x00000000e1234567> (a java.util.HashMap)- locked by "http-nio-8080-exec-12"  // 被其他线程持有

问题代码:

// ❌ 锁粒度太大
public synchronized User getUser(Long id) {return userCache.get(id);
}

修复方案:

// ✅ 使用并发集合
private ConcurrentHashMap<Long, User> userCache = new ConcurrentHashMap<>();public User getUser(Long id) {return userCache.get(id);  // 无锁读
}
四、一键排查脚本(推荐使用)

#!/bin/bash
# 保存为 cpu_diagnosis.shecho "========== CPU飙高诊断 =========="# 1. 找到CPU最高的Java进程
PID=$(ps aux --sort=-%cpu | grep java | grep -v grep | head -1 | awk '{print $2}')
echo "Java进程PID: $PID"# 2. 显示CPU类型
echo -e "\n=== CPU类型分布 ==="
top -bn1 | grep "Cpu(s)" | awk '{print "User:" $2, "System:" $4, "IOWait:" $10}'# 3. 显示Top 5 CPU线程
echo -e "\n=== Top 5 CPU线程 ==="
echo "TID        %CPU    HEX_TID"
ps -mp $PID -o THREAD,tid,time,%cpu | sort -k4 -rn | awk 'NR>1 && NR<=6 {tid=$2; cpu=$4;if (tid != "-") {cmd="printf \"0x%x\" " tid;cmd | getline hex;close(cmd);printf "%-10s %-7s %s\n", tid, cpu"%", hex;}
}'# 4. 导出堆栈
echo -e "\n=== 导出堆栈 ==="
DUMP_FILE="thread_dump_$(date +%H%M%S).txt"
jstack $PID > $DUMP_FILE
echo "堆栈已导出: $DUMP_FILE"# 5. 提示下一步
echo -e "\n=== 下一步 ==="
echo "使用以下命令查看热点线程堆栈:"
echo "grep -A 50 'nid=0x<HEX_TID>' $DUMP_FILE"echo -e "\n========== 诊断完成 =========="

使用方法:


# 1. 保存脚本
vim cpu_diagnosis.sh
# 粘贴上述脚本内容# 2. 添加执行权限
chmod +x cpu_diagnosis.sh# 3. 执行(需要root或应用用户权限)
sudo ./cpu_diagnosis.sh

复杂版:

#!/bin/bash
# cpu_diagnosis.sh - CPU飙高自动诊断脚本set -e# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Colorecho -e "${GREEN}========================================${NC}"
echo -e "${GREEN}   CPU飙高问题自动诊断工具 v1.0${NC}"
echo -e "${GREEN}========================================${NC}"
echo ""# 1. 找到CPU最高的Java进程
echo -e "${YELLOW}[Step 1] 定位问题进程...${NC}"
JAVA_PID=$(ps aux --sort=-%cpu | grep java | grep -v grep | head -1 | awk '{print $2}')if [ -z "$JAVA_PID" ]; thenecho -e "${RED}错误:未找到Java进程${NC}"exit 1
fiJAVA_CMD=$(ps -p $JAVA_PID -o cmd --no-headers | cut -c1-80)
CPU_USAGE=$(ps -p $JAVA_PID -o %cpu --no-headers)echo -e "  ${GREEN}${NC} 目标进程: PID=${JAVA_PID}"
echo -e "  ${GREEN}${NC} CPU使用率: ${CPU_USAGE}%"
echo -e "  ${GREEN}${NC} 命令: $JAVA_CMD"
echo ""# 2. 显示CPU类型分布
echo -e "${YELLOW}[Step 2] 分析CPU类型分布...${NC}"
top -bn1 | grep "Cpu(s)" | \
awk -v red="$RED" -v green="$GREEN" -v yellow="$YELLOW" -v nc="$NC" '{us=$2; sy=$4; wa=$10; id=$8;printf "  用户态(us): " us " ";if (us+0 > 80) printf red "⚠️ 高" nc; else printf green "✓" nc;printf "\n";printf "  系统态(sy): " sy " ";if (sy+0 > 30) printf red "⚠️ 高" nc; else printf green "✓" nc;printf "\n";printf "  IO等待(wa): " wa " ";if (wa+0 > 20) printf red "⚠️ 高" nc; else printf green "✓" nc;printf "\n";printf "  空闲(id): " id "\n";
}'
echo ""# 3. 找出Top 5 CPU线程
echo -e "${YELLOW}[Step 3] 定位热点线程...${NC}"
echo "  TID        CPU%    HEX_TID"
echo "  --------------------------------"
ps -mp $JAVA_PID -o THREAD,tid,time,%cpu --sort=-%cpu | awk 'NR>1 && NR<=6 {tid=$2; cpu=$4;if (tid != "-") {cmd="printf \"0x%x\" " tid;cmd | getline hex;close(cmd);printf "  %-10s %-7s %s\n", tid, cpu"%", hex;}
}'
echo ""# 4. 快速检查GC情况
echo -e "${YELLOW}[Step 4] 检查GC状态...${NC}"
if command -v jstat &> /dev/null; thenjstat -gc $JAVA_PID 1000 3 2>/dev/null | tail -2 | \awk 'NR==1{ou=$7; oc=$8; fgc=$14; fgct=$15;usage=ou/oc*100;printf "  Old区使用率: %.1f%%", usage;if (usage > 95) printf " " red "⚠️ 接近满" nc;printf "\n";printf "  Full GC次数: %s (总耗时: %ss)\n", fgc, fgct;}'
elseecho -e "  ${YELLOW}⚠ jstat未安装,跳过GC检查${NC}"
fi
echo ""# 5. 导出线程堆栈
echo -e "${YELLOW}[Step 5] 导出线程堆栈...${NC}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DUMP_DIR="cpu_issue_$TIMESTAMP"
mkdir -p $DUMP_DIRif command -v jstack &> /dev/null; thenfor i in {1..3}; dojstack $JAVA_PID > "$DUMP_DIR/thread_dump_$i.txt" 2>/dev/nullecho -e "  ${GREEN}${NC} thread_dump_$i.txt"sleep 2done# 统计线程状态echo ""echo "  线程状态分布:"jstack $JAVA_PID 2>/dev/null | grep "java.lang.Thread.State" | sort | uniq -c | \while read count state; doprintf "    %-30s: %s\n" "$state" "$count"done
elseecho -e "  ${RED}✗ jstack未安装${NC}"
fi
echo ""# 6. 系统信息
echo -e "${YELLOW}[Step 6] 导出系统信息...${NC}"
top -H -p $JAVA_PID -b -n 1 > "$DUMP_DIR/top.txt" 2>/dev/null && \echo -e "  ${GREEN}${NC} top.txt"
ps -mp $JAVA_PID -o THREAD,tid,time,%cpu --sort=-%cpu > "$DUMP_DIR/ps_threads.txt" 2>/dev/null && \echo -e "  ${GREEN}${NC} ps_threads.txt"
vmstat 1 5 > "$DUMP_DIR/vmstat.txt" 2>/dev/null && \echo -e "  ${GREEN}${NC} vmstat.txt"
echo ""# 7. 打包
echo -e "${YELLOW}[Step 7] 打包诊断文件...${NC}"
tar czf "${DUMP_DIR}.tar.gz" $DUMP_DIR 2>/dev/null
echo -e "  ${GREEN}${NC} ${DUMP_DIR}.tar.gz"
echo ""# 8. 生成分析建议
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}   诊断完成!${NC}"
echo -e "${GREEN}========================================${NC}"
echo ""
echo -e "${YELLOW}📋 下一步操作:${NC}"
echo ""
echo "1. 查看热点线程堆栈:"
echo "   grep -A 50 'nid=0x<HEX_TID>' $DUMP_DIR/thread_dump_1.txt"
echo ""
echo "2. 搜索常见问题特征:"
echo "   grep -r 'Pattern\$Loop' $DUMP_DIR/"
echo "   grep -r 'BLOCKED' $DUMP_DIR/"
echo ""
echo "3. 如果怀疑GC问题:"
echo "   jmap -heap $JAVA_PID"
echo "   jmap -histo:live $JAVA_PID | head -20"
echo ""
echo "4. 生成火焰图(推荐):"
echo "   java -jar arthas-boot.jar"
echo "   profiler start && sleep 30 && profiler stop --format html"
echo ""
echo -e "${YELLOW}📦 诊断文件已保存到: ${DUMP_DIR}.tar.gz${NC}"

五、高级工具:Arthas(强烈推荐)

Arthas是阿里开源的Java诊断神器,无需修改代码、无需重启应用。

安装使用:


# 1. 下载
wget https://arthas.aliyun.com/arthas-boot.jar# 2. 启动(自动列出Java进程,选择目标进程)
java -jar arthas-boot.jar# 3. 核心命令
# 查看CPU最高的3个线程(自动显示堆栈)
thread -n 3# 查看指定线程
thread <tid># 查找死锁
thread -b# 实时监控面板
dashboard# 生成火焰图(最直观)
profiler start
# 等待30-60秒
profiler stop --format html
# 生成 arthas-output/profiler-timestamp.html

Arthas优势:
✅ 自动找到高CPU线程
✅ 自动显示完整堆栈
✅ 生成火焰图(可视化)
✅ 无需TID转16进制
✅ 实时监控

六、预防措施

  1. 代码层面:
✅ 循环优化□ 避免while(true)空转,使用阻塞等待□ 避免在循环内创建对象(如Pattern.compile)□ 使用懒加载,按需计算✅ 正则表达式□ 避免嵌套量词:(a+)+、(a*)*□ 输入长度限制(如<100字符)□ 复杂正则添加超时保护✅ 集合操作□ 并发场景使用ConcurrentHashMap□ 指定集合初始容量,避免扩容□ 大集合分批处理✅ 数据库□ 避免N+1查询,使用JOIN□ 使用分页查询(LIMIT)□ 添加合适索引□ 使用连接池✅ 并发控制□ 锁粒度最小化□ 使用ConcurrentHashMap等并发集合□ 避免在synchronized内调用外部方法□ 线程池配置合理:核心数=CPU核数*2✅ 资源管理□ 及时关闭资源(try-with-resources)□ 使用对象池(数据库连接、HTTP连接)□ 避免内存泄漏(静态集合、ThreadLocal)
  1. JVM参数:

# 生产环境推荐配置
java -server \-Xms8g -Xmx8g \                              # 堆内存初始=最大-Xmn2g \                                     # 年轻代2GB-XX:MetaspaceSize=256m \                     # 元空间-XX:MaxMetaspaceSize=512m \                  # 元空间最大值-XX:+UseG1GC \                               # G1垃圾收集器-XX:MaxGCPauseMillis=200 \                   # GC暂停目标200ms-XX:G1HeapRegionSize=16m \                   # G1 Region大小-XX:+HeapDumpOnOutOfMemoryError \            # OOM时自动Dump-XX:HeapDumpPath=/data/logs/heap_dump.hprof \-XX:+PrintGCDetails \                        # 打印GC详情-XX:+PrintGCDateStamps \                     # 打印GC时间戳-Xloggc:/data/logs/gc_%p_%t.log \            # GC日志路径-XX:+UseGCLogFileRotation \                  # GC日志滚动-XX:NumberOfGCLogFiles=10 \                  # 保留10个GC日志-XX:GCLogFileSize=100M \                     # 每个日志100MB-XX:+UnlockDiagnosticVMOptions \             # 解锁诊断选项-XX:+PrintSafepointStatistics \              # 打印安全点统计-XX:PrintSafepointStatisticsCount=1 \        # 每次都打印-jar app.jar

八、快速参考卡

CPU飙高排查5步速查表

  • 1️⃣ ps aux --sort=-%cpu → 找进程PID
  • 2️⃣ ps -mp -o THREAD → 找线程TID
  • 3️⃣ printf “0x%x” → TID转16进制
  • 4️⃣ jstack → 导出堆栈
  • 5️⃣ grep “nid=0x” → 定位代码

快捷命令:

  • ps -mp -o THREAD,tid,%cpu | sort -k4 -rn
  • jstack | grep -A 50 “nid=0x”

神器:Arthas

  • java -jar arthas-boot.jar
  • thread -n 3
  • profiler start && profiler stop

经验总结:

  • 90%的CPU飙高都是代码问题(死循环、正则、GC)
  • 堆栈连续导出3次对比分析更准确
  • 保留现场比快速重启更重要
  • 预防胜于治疗,代码审查+监控告警

最后忠告:线上问题,先保留现场,再重启应用!

http://www.dtcms.com/a/579392.html

相关文章:

  • 网站建设技术是干嘛的网站建立步骤
  • 智能建站模版国内专业建站平台
  • app会替代网站吗建设营销型网站的原因
  • 深圳建站哪家专业建网站自己做服务器
  • 建购物网站需要多少钱建设银行网站怎么短信转账
  • 西安长安区网站优化地址网站布局英文
  • Linux实用操作
  • 网站域名免费注册深圳宝安房价
  • 如何搜索网站微信公众号平台登陆
  • 销售外包团队搜索关键词排名优化技术
  • 常见网站推广方式莱芜吧重汽怎么样
  • 网站维护怎么学视频制作软件手机版
  • 怎么看到网站开发时间永嘉高端网站建设效果
  • 怎么建一个网站卖东西咸宁有做网站的吗
  • 抚州做网站wordpress菜单页面未找到
  • 注册域名的网站有哪些有没有网站
  • 上海做网站的小公司有哪些网站接入支付宝需要网站备案吗
  • 商品网站开发需求表企业定制网站建设公司
  • 二手表网站2012版wordpress
  • 怎么做付款链接网站中国公路建设行业协会网站上
  • Redis缓存“防坑指南”:从“缓存穿透”到“分布式锁”,3个案例讲透实战用法
  • seo网站排名如何查看网站流量
  • 网站优化的基本思想百度发布信息怎么弄
  • 什么专业可以做网站编辑游戏软件开发培训
  • 北大荒建设集团有限公司网站求一个手机能看的2022
  • 免费建站并且绑定域名怎么在网上做外贸
  • 本地wordpress站点上传下载 app
  • 深圳网站设计制海口 网站建设
  • 网站订单系统模板淘宝美工做倒计时图片网站
  • 上海的网站开发公司电话电脑广告设计软件