尽管内存充足,但巨大的阵列仍会抛出内存

2022-09-03 13:14:49

使用该标志提供 1 GB 的堆,以下内容按预期工作:-Xmx1G

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];
    }
}

尽管数组应表示大约800 MB,因此很容易放入内存中。

缺失的记忆去哪儿了?


答案 1

在 Java 中,堆中通常有多个区域(和子区域)。你有一个年轻和终身制的地区,大多数收藏家。大型数组会立即添加到永久区域,但是根据您的最大内存大小,将为年轻空间保留一些空间。如果您缓慢分配内存,这些区域将调整大小,但是像这样的大块可能会像您所看到的那样失败。

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

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

IntBuffer array = ByteBuffer.allocateDirect(200*1000*1000*4)
                            .order(ByteOrder.nativeOrder()).asIntBuffer();

int a = array.get(n);
array.put(n, a+1);

它写起来有点乏味,但有一个很大的优势,它几乎不使用堆。(头顶上少于 1 KB)


答案 2

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

是否可以使用使用较小内存块的不同数据结构或多个较小的数组?

例如,以下代码适用于:-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);
        }
    }
}