为什么我的Java堆转储大小比已使用的内存要小得多?

23

问题

我们正在尝试找到我们Web应用程序中一个大的内存泄漏的罪魁祸首。我们对于查找内存泄漏的经验非常有限,但是我们找到了如何使用jmap制作Java堆转储文件并在Eclipse MAT中进行分析。

然而,当我们的应用程序使用56/60GB内存时,堆转储文件只有16GB大小,甚至在Eclipse MAT中更少。

背景

我们的服务器在Ubuntu 14.04上使用Wildfly 8.2.0作为我们Java应用程序的运行环境,其进程使用可用内存的95%。在制作堆转储文件时,我们的缓冲区/高速缓存使用空间为56GB。

我们使用以下命令来创建转储文件:sudo -u {应用程序用户} jmap -dump:file=/mnt/heapdump/dump_prd.bin {pid}

堆转储文件大小为16.4GB,在使用Eclipse MAT进行分析时,它说有大约1GB的活动对象和~14.8GB的不可访问/浅堆。

编辑:这里有关于我们看到发生的问题的一些更多信息。我们监控我们的内存使用情况,并且我们看到它不断增长,直到只剩下约300MB的可用内存。然后它会保持在这个内存量左右,直到进程崩溃,不幸的是应用程序日志中没有错误信息。

这使我们假设这是一个硬OOME错误,因为这仅在内存接近耗尽时才会发生。我们使用-Xms25000m -Xmx40000m的JVM设置。

问题

基本上,我们想知道为什么我们的大部分内存没有在这个转储文件中捕获。最高维持大小类看起来并不太可疑,所以我们想知道是否有一些与堆转储相关的问题,我们做错了什么。


你是如何测量应用程序的内存使用情况的?仅仅因为Java进程使用了X数量的内存,并不意味着Java堆是X - Kayaman
很好,我们正在使用Linux命令free -h来查看我们的内存使用情况。 - Thermometer
2
直到进程崩溃,不幸的是应用程序日志中没有错误 - 检查服务器可执行文件所在的目录;通常这是调用'java'命令的目录,也是JVM创建崩溃报告文件的目录。看看是否存在这样的文件,它可能会给出一些线索。你描述的听起来像是一个严重的虚拟机崩溃,而不是正常的Java应用程序异常。 - Gimby
1
你是否从JVM捕获了stdout和stderr?如果没有,请尝试将它们重定向到文件中,您可能会在那里看到异常信息。 - schtever
感谢Gimby和schtever的建议,非常感激! - Thermometer
显示剩余5条评论
2个回答

29

在转储其堆时,JVM将首先运行垃圾回收循环以释放任何不可达对象。

如何在未先进行垃圾回收的情况下拍摄Java 5中的堆转储?

根据我的经验,在真正的OutOfMemoryError中,您的应用程序需要的堆空间超过了可用空间,这个垃圾回收是徒劳无功的,最终堆转储的大小将等于最大堆大小。

当堆转储远小于最大堆大小时,这意味着系统并非真正没有内存,而是可能存在内存压力。例如,存在java.lang.OutOfMemoryError:GC overhead limit exceeded错误,这意味着JVM可能已经释放了足够的内存来处理一些新的分配请求,但它花费了太多时间来收集垃圾。

也有可能您没有内存问题。是什么让您认为您有内存问题?您没有提及堆使用率或OutOfMemoryError。您只提到了JVM在操作系统上的内存占用情况。


非常好的解释!我已经更新了问题,并回答了你所问的问题。这可能是堆转储文件大小如此之低的原因。 - Thermometer
2
不幸的是,这并不完全正确。只有在使用live子选项进行转储操作时才会发生这种情况:jmap -dump:live,file=... 在这种情况下,为了仅转储活动对象,将进行完整的GC。但事实上,在该命令和mat指示不可达对象已经进入转储时,并没有发生这种情况。这可能是由于多种原因造成的,例如在转储之前进行了完整的GC,宽指针等。 - bric3

6
根据我的经验,堆转储文件比实际内存使用量小很多可能是由于JNI中的泄漏引起的。虽然您没有直接使用任何本地代码,但某些库使用它来加速处理。在我们的案例中,问题出在未正确结束Deflater和Inflater。请参考DeflaterInflater

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接