概述
通过对搜寻集群中的内存一直增长问题的排查,总结排查内存方面的办法和教训。以便记录和参考。
问题体现
- 公布之后机器内存一直上涨。须要重启能力失去解决。
解决过程
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…