当前位置: 首页 > 后端技术 > Java

【故障排除系列】JDK1.8下内存持续增长的故障排除及解决方法

时间:2023-04-01 17:21:36 Java

概述通过排查搜索集群内存持续增长的问题,总结排查内存的方法和经验。供记录和参考。问题是释放后机器内存一直在上升。需要重启才能解决。解决过程jmap排查堆内存阶段使用传统方式dump内存,然后使用Jprofiler或者mat进行分析。这里采用对比的方式,即重启后立即的dump和运行一天后的dump的快照对比,发现差异很小,采用了dump:live和non-live的方式,并没有发现堆中有明显的异常。.详细的操作方法这里不再赘述。使用NMT查看nativememory需要添加参数-XX:NativeMemoryTracking=detail。据说加了之后性能会下降5%到10%。我添加了它,但它没有下降。不过还是建议在线谨慎,加个机器排查问题就好了。设置NMT的基线:jcmdVM.native_memorybaseline设置基线后,可以标记一个基线内存状态。一段时间后,你可以对比一下内存的变化,看看哪个部分增长最多。一段时间后,使用jcmdVM.native_memorydetail.diffscale=MB查看内存变化。经过一段时间之后变化如下:NativeMemoryTracking:Total:reserved=15048MB+73MB,committed=13993MB+74MB-JavaHeap(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)-线程(reserved=1457MB+5MB,committed=1457MB+5MB)(thread#1444+4)(stack:reserved=1449MB+5MB,committed=1449MB+5MB)(malloc=5MB#7227+20)(arena=3MB#2887+8)-代码(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)-编译器(reserved=5MB,committed=5MB)(malloc=5MB#5673+3)-内部(保留=610MB+3MB,提交=610MB+3MB)(malloc=609MB+3MB#168363+191)-符号(保留=672MB+50MB,提交=672MB+50MB)(malloc=667MB+50MB#465680+6396)(arena=5MB#1)-本机内存跟踪(保留=15MB,提交=15MB)(malloc=1MB#9771+3390)(跟踪开销=15MB)-未知(保留=20MB,提交=0MB)(mmap:reserved=20MB,committed=0MB)可以看出Symbol部分明显增加了。这部分主要存放Stringintern等信息,因此可以初步判断这部分已经泄露。求解决方案这里有一个基础知识,就是jdk8对metaspace的改变。你可以谷歌自己查看更改。jdk8之后去掉了永久代,metaspace存放在nativememory中。在搜索NMT内存泄漏等关键词时,发现jdk中好像有一个bug,会导致局部内存泄漏:https://bugs.openjdk.java.net...可以看出性能是基本一致:可以看jdk1.8131版本发现这个问题,而我们用的是101版本,所以怀疑也存在这个问题,于是尝试修改jdk版本解决这个问题。解决方法是升级jdk,所以升级到jdk1.8.0_202。结果验证同上一步,这次采用对比的方式,即一台机器使用原来的jdk版本(101版本),另一台机器使用202版本。经过2天的运行,可以看到如下差距:很明显是内存申请上的差异,差异主要是Symbol。和jdk中的bug表现基本一致。也可以设置-XX:MaxMetaspaceSize来限制metaspace,不过没测试过。因为内存泄漏的主要原因仍然是bug,而不是过度生成大量元数据或Stringinterned。整体排查思路是使用jmap查看内存情况和内存分配情况。dumpheapsnapshot,分析堆中的情况,检查是否有内存泄漏。注意dump会导致FGC,需要在线和离线进行。而且想了想,如果是堆内存泄漏,不太可能导致物理内存继续增长。因为堆的大小是确定的。确认堆内内存没有问题后,检查堆外内存,用NMT检查,设置基线,定时比较。定位问题后,找到解决方案并尝试解决。尝试解决后,再对比控制变量,确认问题真的解决了。调整后稳定在线运行48小时,确认调整没有带来其他副作用。参考Oracle官方bug信息:https://bugs.java.com/bugdata...JDKbug信息: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...