繁体   English   中英

Java在真正内存不足之前抛出内存不足异常?

[英]Java throwing out of memory exception before it's really out of memory?

我希望创建一个大的 int 数组,它几乎可以填满 JVM 可用的所有内存。 以这段代码为例:

    final int numBuffers = (int) ((runtime.freeMemory() - 200000L) / (BUFFER_SIZE));
    System.out.println(runtime.freeMemory());
    System.out.println(numBuffers*(BUFFER_SIZE/4)*4);
    buffers = new int[numBuffers*(BUFFER_SIZE / 4)];

当以 10M 的堆大小运行时,这会抛出 OutOfMemoryException,尽管 printlns 的输出是:

9487176
9273344

我知道数组会有一些开销,但不是 200k,对吧? 为什么 java 无法为它声称有足够空间的东西分配内存? 在 Java 运行它之前,我必须将减去的常量设置为 4M 左右(此时 printlns 看起来更像是:9487176 5472256)

更令人困惑的是,如果我用二维数组替换缓冲区:

buffers = new int[numBuffers][BUFFER_SIZE / 4];

然后它使用上面显示的 200k 减法毫无怨言地运行 - 即使两个数组中存储的整数数量相同(并且二维数组的开销不会大于一维数组的开销,因为它得到了所有那些对要存储的其他数组的引用)。

有任何想法吗?

VM 会将堆内存划分为不同的区域(主要用于垃圾收集器),因此当您尝试分配几乎整个堆大小的单个对象时,您将耗尽内存。

此外,JRE 已经用完了一些内存。 200k 对于今天的内存大小来说不算什么,10M 堆对于大多数应用程序来说几乎是不切实际的小。

数组的实际开销相对较小,在 32 位 VM 上它的 12 字节 IIRC(如果大小小于最小粒度,即 AFAIK 8 字节,则会浪费什么)。 所以在最坏的情况下,每个数组的开销大约为 19 字节。

请注意,Java 没有 2D(多维)数组,它在内部将其实现为数组的数组。

在 2D 情况下,您正在分配更多、更小的对象。 内存管理器反对单个大对象占用大部分堆。 为什么这是令人反感的是垃圾收集方案的一个细节——这可能是因为类似它的东西可以在几代之间移动较小的对象,而堆不会容纳移动单个大对象。

这可能是由于内存碎片和 JVM 无法在给定当前堆的情况下分配该大小的数组。

假设您的堆有 10 x长:

xxxxxxxxxx 

然后,你在这里分配一个对象0 这使您的堆看起来像:

xxxxxxx0xx

现在,您不能再分配那 10 个x空间。 你甚至不能分配 8 x s,尽管可用内存是 9 x s。

事实上,数组的数组不会遇到同样的问题,因为它不连续。

编辑:请注意,以上是对问题的非常简单的看法。 当堆中需要空间时,Java 的垃圾收集器将尝试收集尽可能多的内存,如果真的、真的有必要,会尝试压缩堆。 但是,某些对象可能不可移动或不可收集,从而产生堆碎片并使您处于上述情况。

您还必须考虑许多其他因素,其中一些包括:VM(不太可能)或您的应用程序(对于简单场景也不太可能)的内存泄漏,使用Runtime.freeMemory()的不可靠性( GC 可能会在调用后立即运行,并且可用的空闲内存可能会发生变化),每个特定 JVM 的实现细节等。

关键是,根据经验,不要总是期望您的应用程序可以使用全部的Runtime.freeMemory()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM