1. Java线上问题排查全景图
当Java应用在生产环境出现内存异常或CPU飙高时,整个排查过程就像医生诊断疑难杂症。我处理过上百起此类线上事故,总结出一套系统性的排查方法论。不同于教科书式的理论讲解,这里分享的都是经过实战验证的"临床经验"。
内存异常和CPU问题往往相伴而生:内存泄漏会导致频繁GC进而引发CPU飙升,而CPU过载又可能因线程阻塞导致对象无法及时释放。我们需要同时准备两套排查工具:内存分析工具(如MAT、JProfiler)和CPU分析工具(如Arthas、async-profiler)。在阿里云等生产环境中,通常需要先通过简单的命令快速定位问题方向,再决定是否下载完整的堆转储文件进行深度分析。
关键提示:线上环境务必保持最小侵入性原则,先用top、vmstat等基础命令确认问题范围,再决定是否使用更复杂的诊断工具。我曾见过因盲目执行jmap -histo导致进程卡死的案例。
2. 内存异常排查实战手册
2.1 快速症状诊断
当收到内存报警时,我通常会按以下顺序快速检查:
bash复制# 查看整体内存情况(重点关注RES和%MEM)
top -c -p <pid>
# 观察JVM内存分布(老年代使用率是关键指标)
jstat -gcutil <pid> 1000 5
# 检查堆外内存(当堆内存正常但RES很高时)
jcmd <pid> VM.native_memory summary
最近处理的一个典型案例:某电商应用频繁Full GC但堆内存使用率仅60%。通过native_memory发现DirectByteBuffer持续增长,最终定位到未关闭的MappedByteBuffer导致堆外内存泄漏。这种情况用常规堆内存分析工具根本无法发现。
2.2 堆转储深度分析
当确认堆内存异常时,获取堆转储文件是必须的。我推荐以下两种方式:
bash复制# 完整转储(适合非紧急情况)
jmap -dump:format=b,file=heap.hprof <pid>
# 紧急情况下的轻量级转储(仅存活对象)
jmap -dump:live,format=b,file=heap.hprof <pid>
使用MAT分析时重点关注:
- Dominator Tree中的大对象
- Leak Suspects自动报告
- OQL查询特定模式的对象
- GC Roots到泄漏对象的引用链
避坑指南:生产环境获取堆转储前务必考虑磁盘空间。有次我遇到300GB的堆转储,差点把磁盘写爆。建议先用jmap -histo查看对象概览。
2.3 常见内存问题模式
根据我的经验统计,Java内存问题主要分为以下几类:
| 问题类型 | 典型特征 | 排查技巧 |
|---|---|---|
| 对象泄漏 | 老年代持续增长,Full GC后不下降 | 对比GC前后的堆转储 |
| 缓存失控 | Cache类对象占比异常高 | 检查缓存淘汰策略 |
| 元数据区溢出 | PermGen/Metaspace持续增长 | 分析类加载器 |
| 线程堆积 | 线程数异常多且不释放 | jstack查看线程栈 |
| 堆外泄漏 | Native Memory持续增长 | NMT工具分析 |
3. CPU飙高排查全流程
3.1 快速定位热点线程
CPU问题的黄金排查组合:
bash复制# 显示线程CPU占用(Shift+H切换线程模式)
top -H -p <pid>
# 将十进制线程ID转为十六进制
printf "%x\n" <tid>
# 获取线程栈(注意十六进制匹配)
jstack <pid> | grep -A 20 <nid>
最近解决的典型案例:某支付系统CPU持续100%,通过top -H发现多个线程卡在DecimalFormat.format()方法。原因是并发环境下SimpleDateFormat线程不安全导致死循环。
3.2 高级Profiling技巧
当简单线程分析无法定位时,需要更专业的工具:
bash复制# Arthas实时热点方法分析
profiler start -d 30 -f /tmp/flamegraph.html
# async-profiler采样(对性能影响小)
./profiler.sh -d 30 -f flamegraph.html <pid>
火焰图分析要点:
- 看最宽的"平顶山"(热点方法)
- 关注可疑的调用链模式
- 对比正常时期的基准火焰图
3.3 典型CPU问题场景
根据我的故障库统计,高频CPU问题包括:
- 死循环:正则表达式回溯、递归缺失终止条件
- 锁竞争:synchronized膨胀、CAS自旋
- 序列化/反序列化:大对象JSON处理
- 算法缺陷:O(n²)复杂度在数据量增长时爆发
- 第三方库缺陷:如旧版本Log4j2异步日志问题
4. 复合问题排查策略
当内存和CPU问题同时出现时,需要特殊的排查策略:
4.1 GC导致的CPU飙升
特征:CPU使用呈锯齿状,与GC日志时间吻合
bash复制# 添加GC日志参数后复现问题
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log
常见诱因:
- 年轻代太小导致频繁Minor GC
- 大对象直接进入老年代
- CMS并发模式失败
4.2 内存泄漏引发的连锁反应
典型案例:某Kafka消费者组因消息堆积导致内存泄漏,进而触发频繁GC,最终CPU持续100%。这类问题需要:
- 先限制内存使用(如降低Kafka fetch.size)
- 再解决根本的内存泄漏
- 最后优化GC参数
5. 预防性设计规范
根据多年经验,我总结了以下编码规范来预防这类问题:
-
对象池管理:
- 避免频繁创建大对象(如ByteBuffer)
- 对数据库连接等重资源实现带超时的获取机制
-
缓存治理:
java复制// 推荐使用Guava Cache的权重控制 CacheBuilder.newBuilder() .maximumWeight(1024*1024*500) .weigher((k,v) -> ((CacheItem)v).getSize()) .build(); -
线程模型优化:
- 合理设置线程池参数(尤其队列类型和大小)
- 对异步任务实现熔断机制
-
监控体系建设:
bash复制# 添加JVM监控指标 -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M
6. 高级工具链配置
我常用的生产级诊断工具配置:
6.1 Arthas进阶用法
bash复制# 监控方法调用统计
monitor -c 5 com.example.Service *
# 追踪调用路径
trace com.example.Service expensiveMethod
# 热修复代码(紧急情况)
redefine /tmp/patch.class
6.2 JVM参数优化模板
bash复制# 生产环境推荐基础配置
-Xms4g -Xmx4g -XX:MetaspaceSize=256m
-XX:+UseG1GC -XX:MaxGCPauseMillis=200
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/path/to/dumps
6.3 容器环境特别注意事项
在K8s环境中需要额外关注:
- 容器内存限制与JVM堆的匹配
- cgroup导致的CPU配额问题
- 容器被杀后诊断信息的保存
bash复制# 在Docker中获取JVM指标
docker stats --no-stream <container>
docker exec -it <container> jcmd 1 VM.flags
7. 经典案例复盘
7.1 线程池不当使用导致内存泄漏
现象:服务重启后内存持续增长,24小时后OOM
根本原因:
java复制// 错误用法:无界队列+固定线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
// 正确做法:使用有界队列并实现拒绝策略
new ThreadPoolExecutor(10, 10,
0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<>(100),
new ThreadPoolExecutor.CallerRunsPolicy());
7.2 HashMap并发扩容导致CPU 100%
现象:并发场景下间歇性CPU飙高
根本原因:多线程同时触发HashMap.resize()
解决方案:改用ConcurrentHashMap或初始化足够容量
7.3 JNI调用引发的堆外泄漏
现象:Native Memory持续增长但堆内存正常
排查步骤:
- 使用NMT确认泄漏区域
- 检查JNI调用的内存释放逻辑
- 最终定位到未释放的C++对象
8. 排查工具箱推荐
我的常用工具清单:
| 工具类型 | 推荐工具 | 适用场景 |
|---|---|---|
| 即时分析 | Arthas | 线上实时诊断 |
| 性能剖析 | async-profiler | 低开销CPU分析 |
| 内存分析 | Eclipse MAT | 堆转储解析 |
| 线程分析 | jstack/jvisualvm | 线程死锁检查 |
| 综合监控 | Prometheus+Grafana | 长期趋势观察 |
特别提示:所有工具都要提前在生产环境测试兼容性。有次紧急排查时才发现容器内glibc版本不兼容async-profiler,耽误了黄金处理时间。
