Appearance
故障诊断-高内存占用、内存泄露
看在前面
一、诊断思路
在 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
二、诊断过程
1. 定位 PID
执行 jps -l 命令,定位高内存占用的 PID
java
jps -l
1174 org.elasticsearch.bootstrap.Elasticsearch2. 使用本机内存跟踪(NMT)追踪内存变化情况
VM 参数配置启用内存跟踪(会导致 5%-10%的性能开销),-XX:NativeMemoryTracking=[off|summary|detail]。
如果在 JVM 退出时打印追踪信息配置:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintNMTStatistics
| jcmd NMT Option | 描述 |
|---|---|
| off | 关闭,默认处于该状态 |
| summary | 收集摘要信息 |
| detail | 收集详细信息 |
执行 jcmd VM.native_memory 追踪统计,命令支持功能如下:
java
jcmd <pid> VM.native_memory [summary|detail|baseline|summary.diff|detail.diff|shutdown] [scale= KB|MB|GB]jcmd NMT Option | 描述 |。 -|-| summary | 收集摘要信息|。 detail | 收集详细信息|。 summary.diff | 根据最后一个基准打印新的摘要报告|。 detail.diff | 根据最后一个基准打印新的详细报告|。 shutdown | 停止本机内存跟踪|。
使用示例:
java
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, 使用 NMT 检测内存泄漏
3. 分析堆直方图、生成 dump 文件
建议使用最新的 jcmd 而不是 jmap 实用程序,以增强诊断功能并降低性能开销。jcmd 命令提供了很多诊断功能,参考上面的处理工具文章链接使用。
下面小节中分别介绍 jcmd、jmap 2 个命令的使用,选择你合适的即可。
dump 文件太大时请注意,为了保证 dump 的信息是可靠的,所以会暂停应用程序。如果 jmap 添加了 :live 参数后,JVM 会先触发 gc,然后再统计信息。
3.1 使用 jcmd 命令分析
- 执行
jcmd PID GC.class_histogram命令查看堆直方图,示例如下:
java
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 文件
3.2 使用 jmap 命令分析
- 执行
jmap -histo:live PID命令查看堆直方图,示例如下:
java
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 工具分析

实际生产过程中我们可以选择更多的工具运行监控、分析 dump 文件: