JavaCPU飙高定位进程线程

wen java案例 1

本文目录导读:

JavaCPU飙高定位进程线程

  1. 快速定位步骤
  2. 完整示例
  3. 高级排查技巧
  4. 常见CPU飙高场景与原因
  5. 应急处理方案
  6. 排查实战清单

当Java应用出现CPU飙高时,定位到具体代码行的标准排查流程如下:

快速定位步骤

1️⃣ 找到CPU最高的Java进程

# 方案1:top命令(推荐)
top -c
# 按大写P(CPU降序),找到进程PID
# 方案2:ps命令
ps aux --sort=-%cpu | head -10

2️⃣ 找到进程内最耗CPU的线程

# 显示该进程的所有线程的CPU使用情况
top -H -p <PID>
# 或使用ps命令
ps -mp <PID> -o THREAD,tid,time | sort -rnk 2 | head -20
# 记录下CPU占用最高的线程ID(TID,十进制)

3️⃣ 将线程ID转换为十六进制

# TID转换为十六进制
printf "%x\n" <TID>
# printf "%x\n" 9876 输出 2694

4️⃣ 生成线程堆栈并定位

# 方案A:jstack(推荐,可多次采样)
jstack <PID> | grep -A 30 <十六进制TID>
# 方案B:打印所有线程堆栈
jstack <PID> > thread_dump.txt
# 然后搜索十六进制TID
# 方案C:实时跟踪
jstack -l <PID> | grep -E "(tid=0x[0-9a-f]+)" | grep -i "0x<十六进制TID>"

完整示例

# 1. 找到CPU高的进程
top -c
# 假设看到 PID=12345 的Java进程CPU > 100%
# 2. 查找该进程内CPU最高的线程
top -H -p 12345
# 假设 TID=23456 占CPU最高
# 3. 转16进制
printf "%x\n" 23456
# 输出: 5ba0
# 4. 查看线程堆栈
jstack 12345 | grep "0x5ba0" -A 30
# 会显示该线程的完整堆栈,定位到具体代码行

高级排查技巧

1 持续监控(采样多次)

# 每2秒采样一次,共5次
for i in {1..5}; do
    jstack <PID> > dump_$i.txt
    sleep 2
done
# 对比多次dump,看哪些线程始终处于RUNNABLE状态

2 使用arthas(阿里开源诊断工具)

# 安装arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar <PID>
# 在arthas控制台中
# 查看当前最忙的线程
thread -n 3 -i 2000
# 跟踪具体线程的堆栈
thread <threadId>
# 监控方法调用耗时
monitor -c 5 com.example.service.UserService getUser

3 使用show-busy-java-threads脚本

# 一键定位最忙线程
wget --no-check-certificate https://raw.githubusercontent.com/oldratlee/useful-scripts/master/show-busy-java-threads
chmod +x show-busy-java-threads
./show-busy-java-threads -p <PID>

常见CPU飙高场景与原因

现象 可能原因 定位线索
一直RUNNABLE 死循环、大量计算 堆栈停在业务代码循环处
大量GC线程 频繁Full GC jstat -gcutil 查看GC情况
线程阻塞 锁竞争激烈 堆栈显示BLOCKED或WAITING
频繁上下文切换 大量短线程 查看线程数、线程dump中有大量新线程

应急处理方案

🔥 紧急降级方案

# 1. 生成堆栈快照
jstack <PID> > /tmp/stack_$(date +%Y%m%d_%H%M%S).txt
# 2. 重启应用(仅限紧急情况)
kill -9 <PID>  # 先强杀
nohup java -jar app.jar > app.log 2>&1 &
# 3. 限流降级(如果能操作)
# 在网关层限制QPS,或者在应用代码中降级

排查实战清单

参考这个流程图:

CPU飙高
  │
  ├── top -c → 找到Java进程PID
  │
  ├── top -H -p <PID> → 找到最忙线程TID
  │
  ├── printf "%x\n" <TID> → 转16进制
  │
  └── jstack <PID> | grep <0xTID> -A 30
       │
       ├── 有业务代码 → 分析代码
       │     常见原因:
       │     ├── 死循环
       │     ├── 大对象/频繁GC
       │     ├── 正则回溯
       │     ├── 序列化/反序列化
       │     └── 频繁的线程创建
       │
       └── 只有JVM内部栈帧 → 
              ├── GC线程 → jstat -gcutil看GC
              ├── Compiler线程 → 可能是JIT编译
              └── VM线程 → 可能是类加载/卸载

最关键的一点:一定要对比多次dump,只看一次dump可能会错过瞬态问题。

抱歉,评论功能暂时关闭!