尽管有足够的可用内存,但巨大的数组会丢失内存

使用-Xmx1G标志提供1千兆字节的堆,以下按预期工作:

 public class Biggy { public static void main(String[] args) { int[] array = new int[150 * 1000 * 1000]; } } 

该数组应代表约600 MB。

但是,以下抛出OutOfMemoryError:

 public class Biggy { public static void main(String[] args) { int[] array = new int[200 * 1000 * 1000]; } } 

尽管arrays应该代表大约800 MB,因此很容易适合内存。

丢失的记忆在哪里消失了?

在Java中,堆中通常有多个区域(和子区域)。 你有一个年轻而终身的地区,拥有大多数collections家。 大型arrays会立即添加到终端区域,但根据您的最大内存大小,将为年轻空间保留一些空间。 如果你慢慢分配内存,这些区域会resize,但像这样的大块可能会像你看到的那样失败。

鉴于内存通常相对便宜(并非总是如此),我只会将最大值增加到您希望应用程序失败的程度,如果它曾经使用过那么多。

顺便说一句:如果你有这样的大型结构,你可以考虑使用直接内存。

 IntBuffer array = ByteBuffer.allocateDirect(200*1000*1000*4) .order(ByteOrder.nativeOrder()).asIntBuffer(); int a = array.get(n); array.put(n, a+1); 

编写它有点乏味,但有一个很大的优点,它几乎不使用堆。 (头顶不到1 KB)

有足够的可用内存,但不是单个连续的内存块,如arrays所需。

您可以使用使用较小内存块或几个较小arrays的不同数据结构吗?

例如,以下代码适用于-Xmx1G

 public class Biggy { public static void main(String[] args) { int [][]array = new int[200][]; for (int i = 0; i < 200; i++) { array[i] = new int[1000 * 1000]; System.out.println("i=" + i); } } } 

堆内存分为三个空格:

  • 老一辈
  • 幸存者空间
  • 伊甸园空间

一开始,这个对象将存在于老一代中,并将在这里停留一段时间。

默认情况下,虚拟机会在每个集合中增大或缩小堆,以尝试将可用空间的比例保持在特定范围内每个集合的活动对象。 该目标范围通过参数-XX:MinHeapFreeRatio =和-XX:MaxHeapFreeRatio =设置为百分比,并且总大小在-Xms以下由-Xms以上限制。

我的jvm中的默认比率是30/70,因此旧一代对象的最大大小是有限的(使用-Xmx1G)700Mb(顺便说一下,当使用默认的jvm参数运行时,我得到相同的exception)。

但是,您可以使用jvm选项调整代数。 例如,您可以使用参数-Xmx1G -XX:NewRatio=10运行您的类-Xmx1G -XX:NewRatio=10new int[200 * 1000 * 1000]; 将会成功。

从我可以说Java不是为了在内存中保存大对象而设计的。 应用程序中内存的典型用法是一堆相对较小的对象的图形,通常只有在所有空间中空间不足时才会得到OutOfMemoryError。

下面是一些有用的(和有趣的阅读)文章:

5.0 Java [tm]虚拟机中的人机工程学

使用5.0 Java [tm]虚拟机调整垃圾收集