专栏原创出处: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 命令分析

  1. 执行 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 分钟一次),则您可能能够观察到可以进行进一步分析的趋势。

  1. 执行 jcmd PID GC.heap_dump filename=filename 生成 dump 文件

2.2) 使用 jmap 命令分析

  1. 执行 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)
   
... 更多内容未粘贴   
  1. 执行 jmap -dump:live,format=b,file=test.dump PID 将存活对象转换为 dump 文件进行离线分析

# 4. dump 文件分析

JDK 自带的 jvisualvm 工具进行分析

jprofile 工具进行分析

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

更多 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

# 专栏更多文章笔记

最后修改时间: 2/17/2020, 4:43:04 AM