5

【问题排查系列】JDK1.8 下内存不断增长排查及解决

 2 years ago
source link: https://segmentfault.com/a/1190000041272695
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【问题排查系列】JDK1.8 下内存不断增长排查及解决

通过对搜索集群中的内存不断增长问题的排查,总结排查内存方面的方法和经验。以便记录和参考。

  • 发布之后机器内存不断上涨。需要重启才能得到解决。

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...
  • 可以看出表现基本一致:

image
image

  • 可以看到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,确认调整没有带来其他副作用。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK