为啥我的 Java 堆转储大小比已用内存小得多?

Posted

技术标签:

【中文标题】为啥我的 Java 堆转储大小比已用内存小得多?【英文标题】:Why is my Java heap dump size much smaller than used memory?为什么我的 Java 堆转储大小比已用内存小得多? 【发布时间】:2015-11-23 03:43:56 【问题描述】:

问题

我们正试图找出 Web 应用程序中出现大内存泄漏的罪魁祸首。我们在查找内存泄漏方面的经验非常有限,但我们发现了如何使用jmap 进行 Java 堆转储并在 Eclipse MAT 中对其进行分析。

但是,我们的应用程序使用 56/60GB 内存,堆转储的大小只有 16GB,在 Eclipse MAT 中甚至更小。

上下文

我们的服务器在 Ubuntu 14.04 上为我们的 java 应用程序使用 Wildfly 8.2.0,其进程使用了​​ 95% 的可用内存。进行转储时,我们的缓冲区/缓存使用空间为 56GB。

我们使用以下命令创建转储:sudo -u application user jmap -dump:file=/mnt/heapdump/dump_prd.bin pid

堆转储文件大小为 16.4GB,在使用 Eclipse MAT 分析时,它显示大约有 1GB 活动对象和约 14.8GB 无法访问/浅堆。

编辑:以下是有关我们看到的问题的更多信息。我们监控我们的内存使用情况,我们看到它不断增长,直到剩下大约 300mb 的可用内存。然后它会停留在该内存量附近,直到进程崩溃,不幸的是应用程序日志中没有错误。

这使我们假设这是一个严重的 OOM 错误,因为这只发生在内存接近耗尽时。我们为我们的 JVM 使用设置 -Xms25000m -Xmx40000m

问题

基本上,我们想知道为什么我们的大部分内存没有在此转储中捕获。***保留大小类看起来不太可疑,所以我们想知道是否有一些与堆转储相关的东西我们做错了什么。

【问题讨论】:

您如何测量应用程序的内存使用情况?仅仅因为Java进程使用X的内存量,并不意味着Java堆是X 好的,我们正在使用linux命令free -h来查看我们的内存使用情况。 "直到进程崩溃,不幸的是应用程序日志中没有错误" - 检查包含服务器可执行文件的目录;这通常是调用“java”命令的目录,也是 JVM 将创建崩溃报告文件的目录。看看是否存在这样的文件,它可能会提供线索。您所描述的听起来像是虚拟机硬崩溃,而不是普通的 Java 应用程序异常。 您是否正在从 JVM 捕获标准输出和标准错误?如果没有,请尝试将它们重定向到文件,您可能会在那里看到异常。 感谢 Gimby 和 schtever 的建议,非常感谢! 【参考方案1】:

根据我的经验,堆转储比实际使用的内存小得多可能是由于 JNI 中的泄漏。

尽管您不直接使用任何本机代码,但某些库使用它来加速。

在我们的例子中,Deflater 和 Inflater 没有正确结束。

【讨论】:

【参考方案2】:

当转储其堆时,JVM 将首先运行垃圾回收周期以释放任何无法访问的对象。

How can I take a heap dump on Java 5 without garbage collecting first?

根据我的经验,在真正的 OutOfMemoryError 中,您的应用程序只是要求比可用的堆空间更多,这个 GC 是愚蠢的差事,最终的堆转储将是最大值的大小。堆大小。

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

您也可能没有内存问题。是什么让你觉得你做的?您没有提及有关堆使用或 OutOfMemoryError 的任何内容。您只提到了 JVM 在操作系统上的内存占用。

【讨论】:

很好的解释!我已经用你问的问题的答案更新了这个问题。这可能是堆转储的大小如此之小的原因。 不幸的是,这并不完全正确。只有当转储操作与live 子选项一起使用时才会发生这种情况:jmap -dump:live,file=... 在这种情况下,将有一个完整的 GC 以便仅转储活动对象。这恰恰没有发生,命令和垫子都指示无法访问的对象在转储中生成。有几个原因可以在转储之前的某个时间发生 ful GC,宽指针...

以上是关于为啥我的 Java 堆转储大小比已用内存小得多?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 windbg> !EEHeap -gc 显示的托管堆比 VMMAP.exe 小得多?

从 Java 堆转储中获取已使用和释放的内存

为啥在 Python 3 中实例的 __dict__ 的大小要小得多?

Java 使用的内存比堆大小(或正确大小的 Docker 内存限制)多得多

为啥同一本书的 ePub 文件比 mobi 或 PDF 文件小得多

分析大型 Java 堆转储 - 内存错误