Java常见性能问题排查
性能问题排查需要系统性方法,从现象定位根因。
问题分类
| 类型 | 现象 | 排查工具 |
|---|---|---|
| CPU问题 | CPU占用高 | top, Arthas |
| 内存问题 | OOM, GC频繁 | jmap, MAT |
| 线程问题 | 死锁, 阻塞 | jstack, Arthas |
| IO问题 | IO慢, 文件大 | iostat, 日志 |
| 网络问题 | 连接慢, 超时 | netstat, tcpdump |
CPU问题排查
CPU占用高
Bash
# 1. 找出高CPU进程
top -H
# 2. 找出Java进程CPU高的线程
top -H -p <pid>
# 3. 转换线程ID为16进制
printf "%x" <thread_id>
# 4. jstack查看线程栈
jstack <pid> | grep <hex_id> -A 20
# 或使用Arthas
thread -n 5 # 显示最忙5个线程
热点方法定位
Bash
# Arthas trace追踪
trace com.example.Service.method '#cost > 100'
# 输出
`---[120.45ms] com.example.Service:method()
+---[80.23ms] com.example.Dao:query()
+---[35.12ms] com.example.Util:parse()
CPU问题常见原因
| 原因 | 解决方案 |
|---|---|
| 死循环 | 检查循环条件 |
| 频繁GC | 调整内存参数 |
| 正则表达式 | 优化或预编译 |
| 序列化 | 换高效序列化 |
| 算法问题 | 优化算法 |
内存问题排查
内存泄漏排查
Bash
# 1. 导出堆快照
jmap -dump:format=b,file=heap.hprof <pid>
# 2. MAT分析
# Leak Suspects Report找嫌疑对象
# Dominator Tree看大对象
# Path to GC Roots看引用链
# 3. 对象数量统计
jmap -histo:live <pid>
MAT分析步骤
Bash
┌─────────────────────────────────────┐
│ MAT分析流程 │
├─────────────────────────────────────┤
│ │
│ 1. 打开堆快照 │
│ │
│ 2. Leak Suspects Report │
│ 查看内存泄漏嫌疑 │
│ │
│ 3. Dominator Tree │
│ 查看对象大小排名 │
│ │
│ 4. 右键对象 → Path to GC Roots │
│ 查看为何无法回收 │
│ │
│ 5. 分析引用链,定位代码 │
│ │
└─────────────────────────────────────┘
GC问题排查
Bash
# 1. 查看GC统计
jstat -gcutil <pid> 1000
# 2. 分析GC日志
# GCEasy.io在线分析
# 3. 常见GC问题
# - 年轻代太小:增大-Xmn
# - 大对象频繁:增大老年代或-XX:PretenureSizeThreshold
# - System.gc()调用:禁止-XX:+DisableExplicitGC
内存问题常见原因
| 原因 | 解决方案 |
|---|---|
| 对象未释放 | 检查引用,及时清理 |
| 缓存无限制 | 设置上限和过期 |
| 连接未关闭 | try-finally确保关闭 |
| ThreadLocal泄漏 | 及时remove |
| 静态集合堆积 | 定期清理或换弱引用 |
线程问题排查
死锁排查
Bash
# jstack自动检测死锁
jstack <pid>
# 输出
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor... which is held by "Thread-2"
"Thread-2":
waiting to lock monitor... which is held by "Thread-1"
# Arthas
thread -b # 找阻塞线程
线程阻塞排查
Bash
# 查看线程状态
jstack <pid> | grep "BLOCKED"
# Arthas thread
thread # 所有线程状态
thread <id> # 指定线程详情
线程状态解读
| 状态 | 说明 |
|---|---|
| RUNNABLE | 运行或等待CPU |
| BLOCKED | 等待锁 |
| WAITING | 等待通知 |
| TIMED_WAITING | 定时等待 |
| parking | LockSupport.park |
线程问题常见原因
| 原因 | 解决方案 |
|---|---|
| 死锁 | 避免锁嵌套,统一锁顺序 |
| 等待数据库 | 优化SQL,连接池 |
| 等待IO | 异步IO,批量处理 |
| 锁竞争激烈 | 减少锁范围,分段锁 |
| 线程池满 | 增大线程池,优化任务 |
IO问题排查
磁盘IO排查
Bash
# 查看IO统计
iostat -x 1
# 输出
Device rrqm/s wrqm/s r/s w/s await svctm %util
sda 0 0 100 0 10 5 50
关键指标:
- %util > 80%:IO压力大
- await > 20ms:IO延迟高
# 查看进程IO
iotop -o
文件问题排查
Bash
# 查看打开文件数
lsof -p <pid> | wc -l
# 查看文件句柄限制
ulimit -n
# 大文件查找
find /data -size +100M -type f
IO问题常见原因
| 原因 | 解决方案 |
|---|---|
| 频繁小IO | 批量读写,缓冲 |
| 大文件处理 | 分块处理,NIO |
| 日志量大 | 异步日志,压缩 |
| 文件句柄泄漏 | 确保关闭,try-finally |
网络问题排查
连接问题排查
Bash
# 查看连接状态
netstat -an | grep <port>
# 连接状态统计
netstat -an | awk '/tcp/ {print $6}' | sort | uniq -c
输出:
100 ESTABLISHED
50 TIME_WAIT
10 SYN_SENT
# 查看TCP队列
netstat -s | grep "listen queue"
连接状态说明
| 状态 | 说明 | 问题 |
|---|---|---|
| ESTABLISHED | 正常连接 | - |
| TIME_WAIT | 连接关闭等待 | 过多需调优 |
| SYN_SENT | 发送SYN等待 | 网络慢 |
| CLOSE_WAIT | 等待关闭 | 未主动关闭 |
网络问题常见原因
| 原因 | 解决方案 |
|---|---|
| 连接超时 | 调整超时参数 |
| TIME_WAIT多 | 开启tw_reuse |
| CLOSE_WAIT多 | 确保连接关闭 |
| 网络慢 | 优化协议,压缩数据 |
排查流程总结
text
┌─────────────┐
│ 发现问题 │ ← 监控告警、用户反馈
└─────────────┘
↓
┌─────────────┐
│ 确认类型 │ ← CPU/内存/线程/IO
└─────────────┘
↓
┌─────────────┐
│ 收集数据 │ ← jstat/jstack/jmap/日志
└─────────────┘
↓
┌─────────────┐
│ 分析定位 │ ← MAT/Arthas/GCEasy
└─────────────┘
↓
┌─────────────┐
│ 确认根因 │ ← 关联代码,找出问题
└─────────────┘
↓
┌─────────────┐
│ 解决验证 │ ← 修复代码,验证效果
└─────────────┘
Arthas一站式排查
text
# 综合面板
dashboard
# CPU热点
thread -n 5
# 方法追踪
trace com.example.Service.method
# 内存监控
memory
# 线程详情
thread <id>
# 反编译查看
jad com.example.Service
注意事项
排查前先保留现场:堆快照、线程栈、GC日志
生产排查工具可能影响性能,谨慎使用
多工具结合分析,单一工具可能遗漏
问题复现后才能彻底解决,临时方案要记录
排查经验很重要,平时多积累
要点总结
- CPU高用top/Arthas thread找热点线程和方法
- 内存泄漏用MAT分析堆快照,找引用链
- 死锁用jstack或Arthas thread -b检测
- IO慢用iostat监控,检查%util和await
- Arthas一站式排查,生产环境推荐
📝 发现内容有误?点击此处直接编辑