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

问题

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

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

上下文

我们的服务器在 Ubuntu 14.04 上使用 Wildfly 8.2.0 作为我们的 java 应用程序,其进程使用 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

问题

基本上,我们想知道为什么我们的大部分内存都没有在这个转储中被捕获。保留最多的尺寸类看起来不太可疑,因此我们想知道是否存在与堆转储相关的错误。


答案 1

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

如何在Java 5上进行堆转储而不先进行垃圾收集?

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

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

也有可能您没有内存问题。是什么让你认为你这样做?你没有提到任何关于堆使用或 OutOfMemoryError 的信息。您只提到了 JVM 在操作系统上的内存占用。


答案 2

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

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

在我们的例子中,它是一个放气器充气机没有正确结束。


推荐