专栏原创出处:github-源笔记文件 (opens new window) ,github-源码 (opens new window),欢迎 Star,转载请附上原文出处链接和本声明。
Java JVM-虚拟机专栏系列笔记,系统性学习可访问个人复盘笔记-技术博客 Java JVM-虚拟机 (opens new window)
# 一、诊断思路
在 OOM 触发时,我们的程序可能已经挂掉或者假死了,一般情况我们可能会重启程序一边运行一边进行故障定位。一直循环这个过程,直到定位到根源问题。
1)程序挂掉时,自动生成 dump 文件进行分析。
使用 JVM 参数获取 dump 文件
- -XX:+HeapDumpOnOutOfMemoryError,当 OutOfMemoryError 发生时自动生成 Heap Dump 文件。
- -XX:+HeapDumpBeforeFullGC,当 JVM 执行 FullGC 前执行 dump。
- -XX:+HeapDumpAfterFullGC,当 JVM 执行 FullGC 后执行 dump。
- -XX:+HeapDumpOnCtrlBreak,交互式获取 dump。在控制台按下快捷键 Ctrl + Break 时,JVM 就会转存一下堆快照。
- -XX:HeapDumpPath=/test.hprof,指定 dump 文件存储路径。
2)程序运行时,同步进行故障诊断,大体思路为:
哪些对象导致堆空间增长(堆空间直方图)
哪些对象不能被垃圾回收
GC 回收的表现,年轻代与老年代的回收频率结果,Full GC 的触发时机
使用本机内存跟踪(NMT)实时比对各内存区域增减情况
诊断过程发现一定规律后,尝试生成 dump 文件进行分析
3)分析 dump 文件思路
- 哪些对象较大,数量较多
- 大对象的占用率
- 大对象的引用栈,分析无效引用
本文内容从如何观察堆空间直方图及 dump 文件分析为主要点进行分析
JDK 相关命令详细用法可参考 Java JVM JDK13 诊断命令处理工具 jps,jstat,jinfo,jmap,jstack,jcmd (opens new window)
# 二、诊断过程
# 1. 定位 PID
执行 jps -l
命令,定位高内存占用的 PID
jps -l
1174 org.elasticsearch.bootstrap.Elasticsearch
# 2. 使用本机内存跟踪(NMT)追踪内存变化情况
VM 参数配置启用内存跟踪(会导致 5%-10%的性能开销),-XX:NativeMemoryTracking=[off|summary|detail]
。
如果在 JVM 退出时打印追踪信息配置:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics
jcmd NMT Option | 描述 |
---|---|
off | 关闭,默认处于该状态 |
summary | 收集摘要信息 |
detail | 收集详细信息 |
执行 jcmd <pid> VM.native_memory
进行追踪统计,命令支持功能如下:
jcmd <pid> VM.native_memory [summary|detail|baseline|summary.diff|detail.diff|shutdown] [scale= KB|MB|GB]
jcmd NMT Option | 描述 |
---|---|
summary | 打印摘要信息 |
detail | 打印详细信息 |
baseline | 创建一个新的内存使用情况基准快照以进行比较 |
summary.diff | 根据最后一个基准打印新的摘要报告 |
detail.diff | 根据最后一个基准打印新的详细报告 |
shutdown | 停止本机内存跟踪 |
使用示例:
jcmd 5460 VM.native_memory baseline ———— 创建一个基准快照
jcmd 5460 VM.native_memory detail.diff —————— 一段时间后进行比较
———————— 部分打印信息如下:
Total: reserved=664624KB -20610KB, committed=254344KB -20610KB <--- 与基线比较,+增加,-减少
- Java Heap (reserved=516096KB, committed=204800KB)
(mmap: reserved=516096KB, committed=204800KB)
- Class (reserved=6578KB +3KB, committed=4530KB +3KB)
(classes #668 +3) <--- 3 个类被加载
(malloc=434KB +3KB, #930 -7) <--- malloc 的内存增加了 3KB,但是 malloc 的数量减少了 7
(mmap: reserved=6144KB, committed=4096KB)
...
...
更多内容可参考官方文档 Native Memory Tracking (opens new window) , 使用 NMT 检测内存泄漏 (opens new window)
# 3. 分析堆直方图、生成 dump 文件
建议使用最新的 jcmd 而不是 jmap 实用程序,以增强诊断功能并降低性能开销,jcmd 命令提供了很多诊断功能,参考上面的处理工具文章链接使用。
下面小节中分别介绍 jcmd、jmap 2 个命令的使用,选择你合适的即可。
dump 文件太大时请注意,为了保证 dump 的信息是可靠的,所以会暂停应用程序。如果 jmap 添加了 :live 参数后,JVM 会先触发 gc,然后再统计信息。
2.1) 使用 jcmd 命令分析
- 执行
jcmd PID GC.class_histogram
命令查看堆直方图,示例如下:
jcmd 11704 GC.class_histogram
11704:
num #instances #bytes class name (module)
-------------------------------------------------------
1: 2007799 48573456 [B (java.base@11.0.5)
2: 2007232 48173568 java.lang.String (java.base@11.0.5)
3: 2000000 48000000 io.gourd.java.jvm.oom.Oom$OomKey
4: 1003838 32122816 java.util.HashMap$Node (java.base@11.0.5)
5: 322 8435136 [Ljava.util.HashMap$Node; (java.base@11.0.5)
输出显示堆中每种类类型的总大小和实例计数。如果获得了一系列直方图(例如,每 2 分钟一次),则您可能能够观察到可以进行进一步分析的趋势。
- 执行
jcmd PID GC.heap_dump filename=filename
生成 dump 文件
2.2) 使用 jmap 命令分析
- 执行
jmap -histo:live PID
命令查看堆直方图,示例如下:
jmap -histo:live 1174
num #instances #bytes class name (module)
-------------------------------------------------------
1: 7570 503328 [B (java.base@11.0.2)
2: 6987 167688 java.lang.String (java.base@11.0.2)
3: 1224 150344 java.lang.Class (java.base@11.0.2)
4: 3825 122400 java.util.HashMap$Node (java.base@11.0.2)
... 更多内容未粘贴
- 执行
jmap -dump:live,format=b,file=test.dump PID
将存活对象转换为 dump 文件进行离线分析
# 4. dump 文件分析
JDK 自带的 jvisualvm 工具进行分析
jprofile 工具进行分析
实际生产过程中我们可以选择更多的工具进行运行监控、分析 dump 文件:
- 推荐-Memory Analyzer (MAT) (opens new window)
- 推荐-付费-jprofiler (opens new window)
- 推荐-Flight Recorder-飞行记录仪 (opens new window)
- jhsdb (opens new window)
- jconsole (opens new window)
- Java Mission Control (opens new window)
更多 JDK 相关命令详细用法可参考 Java JVM JDK13 诊断命令处理工具 jps,jstat,jinfo,jmap,jstack,jcmd (opens new window)
更多故障诊断及调优,参考本专栏 Java JVM(JDK13)-专栏文章目录汇总 (opens new window)。
# 三、GC 相关问题诊断
GC 及内存区域占用情况分析可使用 jstat
命令进行分析
比如:执行 jstat -gcutil -t -h 5 PID 500 10
查看各内存区域占用情况
jstat -gcutil -t -h 5 1174 500 10
Timestamp S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
1138.7 0.00 100.00 25.00 22.32 94.42 85.09 29 0.064 4 0.067 0.131
1139.2 0.00 100.00 50.00 22.32 94.42 85.09 29 0.064 4 0.067 0.131
1139.8 0.00 100.00 50.00 22.32 94.42 85.09 29 0.064 4 0.067 0.131
1140.3 0.00 100.00 50.00 22.32 94.42 85.09 29 0.064 4 0.067 0.131
1140.8 0.00 100.00 50.00 22.32 94.42 85.09 29 0.064 4 0.067 0.131
Timestamp S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
1141.3 0.00 100.00 50.00 22.32 94.42 85.09 29 0.064 4 0.067 0.131
1141.8 0.00 100.00 50.00 22.32 94.42 85.09 29 0.064 4 0.067 0.131
1142.3 0.00 100.00 50.00 22.32 94.42 85.09 29 0.064 4 0.067 0.131
1142.8 0.00 100.00 50.00 22.32 94.42 85.09 29 0.064 4 0.067 0.131
1143.3 0.00 100.00 50.00 22.32 94.42 85.09 29 0.064 4 0.067 0.131