乐趣区

关于java:问题排查系列JDK18-下内存不断增长排查及解决

概述

通过对搜寻集群中的内存一直增长问题的排查,总结排查内存方面的办法和教训。以便记录和参考。

问题体现

  • 公布之后机器内存一直上涨。须要重启能力失去解决。

解决过程

jmap 排查堆内存阶段

  • 应用传统的形式 dump 内存,而后应用 Jprofiler 或 mat 进行剖析。
  • 这里应用了对照的形式,即在重启后立刻 dump 堆和运行一天后 dump 进行快照比对,发现差别较小,且应用了 dump:live 和非 live 两种形式,均未发现堆内有显著异样。
  • 具体操作形式这里不在赘述。

    应用 NMT 排查堆外内存(native memory)

  • 须要增加参数 -XX:NativeMemoryTracking=detail 据说增加了之后性能会降落 5% ~ 10%,我增加了,没降落。不过还是倡议线上谨慎,增加一台机器排查问题即可。
  • 设置 NMT 的基线: jcmd <pid> VM.native_memory baseline 设置基线之后即可标记一个基准内存状态,过一段时间之后能够比拟内存的变动,哪局部增长的最多。
  • 过一段时间后,应用 jcmd <pid> VM.native_memory detail.diff scale=MB 查看内存的变动。
  • 通过一段时间之后变动如下:

Native Memory Tracking:

Total: reserved=15048MB +73MB, committed=13993MB +74MB

-                 Java Heap (reserved=10240MB, committed=10240MB)
                            (mmap: reserved=10240MB, committed=10240MB)

-                     Class (reserved=1224MB, committed=223MB)
                            (classes #30779 +1)
                            (malloc=6MB #100242 +153)
                            (mmap: reserved=1218MB, committed=218MB)

-                    Thread (reserved=1457MB +5MB, committed=1457MB +5MB)
                            (thread #1444 +4)
                            (stack: reserved=1449MB +5MB, committed=1449MB +5MB)
                            (malloc=5MB #7227 +20)
                            (arena=3MB #2887 +8)

-                      Code (reserved=286MB, committed=251MB)
                            (malloc=42MB #41476 +94)
                            (mmap: reserved=244MB, committed=209MB)

-                        GC (reserved=520MB +16MB, committed=520MB +16MB)
                            (malloc=108MB +16MB #153709 +200)
                            (mmap: reserved=412MB, committed=412MB)

-                  Compiler (reserved=5MB, committed=5MB)
                            (malloc=5MB #5673 +3)

-                  Internal (reserved=610MB +3MB, committed=610MB +3MB)
                            (malloc=609MB +3MB #168363 +191)

-                    Symbol (reserved=672MB +50MB, committed=672MB +50MB)
                            (malloc=667MB +50MB #465680 +6396)
                            (arena=5MB #1)

-    Native Memory Tracking (reserved=15MB, committed=15MB)
                            (malloc=1MB #9771 +3390)
                            (tracking overhead=15MB)

-                   Unknown (reserved=20MB, committed=0MB)
                            (mmap: reserved=20MB, committed=0MB)

能够看到其中的 Symbol 局部上涨显著,这部分次要是存储 String intern 等信息。所以能够初步判断是这部分泄露了。

寻找解决方案

  • 这里有个基础知识,即 jdk8 对元空间的变动。能够自行 google 查看变动。jdk8 之后永恒代移除,元空间寄存在 native memory 中。
  • 在搜 NMT 内存泄露等关键字时发现,jdk 仿佛存在 bug,会造成本地内存泄露:https://bugs.openjdk.java.net…
  • 能够看出体现基本一致:


  • 能够看到 jdk1.8 131 版本发现了这个问题,而咱们应用的是 101 版本,所以狐疑也存在这个问题,于是尝试批改 jdk 版本来解决此问题。
  • 解决方案是降级 jdk,于是降级到 jdk1.8.0_202。

后果验证

  • 跟上一步骤一样,这次采纳了比照的形式,即一台机器应用原始的 jdk 版本(101 版本),另外一台应用 202 版本。通过 2 天的运行后,能够看到以下差距:

  • 显著看出进行在申请内存方面的差别,而差别次要来源于 Symbol。和 jdk 中的 bug 体现基本一致。
  • 也能够设置 -XX:MaxMetaspaceSize 对元空间进行限度,不过没有测试。因为目前内存泄露的次要起因还是 bug,而不是过多的产生了大量的元数据或 String interned。

整体排查思路

  • 应用 jmap 查看内存状况,查看内存调配。
  • dump 堆快照剖析堆内状况,排查内存泄露,留神 dump 会 FGC,线上需下线进行。并且起初思考,如果是堆内存泄露其实不太会造成物理上的内存持续增长。因为堆的大小是确定的。
  • 堆内内存确认没有问题之后排查堆外内存,应用 NMT 进行排查,设置 baseline,而后隔段时间进行比对。
  • 定位问题后查找解决方案,尝试解决。
  • 尝试解决后进行控制变量比对,确认问题真的解决。
  • 调整后线上稳固运行 48H,确认调整没有带来其余副作用。

参考资料

  • Oracle 官网 bug 信息:https://bugs.java.com/bugdata…
  • JDK bug 信息:https://bugs.openjdk.java.net…
  • SymbolTable 寄存的内容:https://blog.csdn.net/weixin_…
  • 同样应用 NMT 排查问题的另一示例:https://blog.csdn.net/qiansha…
  • PermGen 与 MetaSpace : https://segmentfault.com/a/11…
  • NMT 应用示例:https://blog.51cto.com/u_1512…
退出移动版