为什么我不能创建大尺寸的数组?

Why I can't create an array with large size?

为什么无法创建最大 int 大小的数组?

int i = 2147483647;
int[] array = new int[i];

我找到了这个解释:

Java arrays are accessed via 32-bit ints, resulting in a maximum theoretical array size of 2147483647 elements.

但是如您所见,我的代码不起作用。也无法创建大小为

的数组
new int[Integer.MAX_VALUE - 5];

技术细节

PS

实际上为什么 -5

一些 VM 在数组中保留了一些 header 个单词。

最大 "safe" 个数会 be 2 147 483 639 (Integer.MAX_VALUE - 8)

Source-http://www.docjar.com/html/api/java/util/ArrayList.java.html

**
  191        * The maximum size of array to allocate.
  192        * Some VMs reserve some header words in an array.
  193        * Attempts to allocate larger arrays may result in
  194        * OutOfMemoryError: Requested array size exceeds VM limit
  195        */
  196       private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

所以这取决于您现在系统上 JVM 可用的最大内存

编辑:为什么显示 OOM。

元素数量 = 2 147 483 639

一个元素所需的字节数 = 4

仅元素的总内存 8589934556 KB == 8.589934555999999 GB

现在如果数组的总内存使用量不是 8 字节的倍数,那么大小将向上舍入到下一个 8 的倍数。

因此,由于开销,您需要的不仅仅是分配的内存,而且应该是连续内存

仅仅有足够的堆来分配是不够的;您需要有一个足够大的堆 region。众所周知,heap是分代的。

对于 8 GB 的单次分配,您必须确保为单个堆区域分配足够多的空间(加上一些开销)。使用 12 GB 的 -Xmx,您可能仍然不够用。使用附加选项来控制老年代的大小。

理论

有两种可能的例外情况:

  • OutOfMemoryError: Java heap space 表示您的数组不适合 java 堆 space。为了解决这个问题,您可以使用 JVM 选项 -Xmx 来增加最大堆大小。还要考虑到 the maximum size of object cannot be larger than the largest heap generation.
  • OutOfMemoryError: Requested array size exceeds VM limit 表示超出了特定于平台的大小:

练习

在 HotSpot JVM 中,数组大小受内部表示的限制。在 GC 代码中,JVM 将堆字中的数组大小作为 int 传递,然后从堆字转换回 jint 这可能会导致溢出。因此,为了避免崩溃和意外行为,最大数组长度受限于 (max size - header size). Where header size 取决于用于构建 JVM 的 C/C++ 编译器,你是 运行(gcc for linux、macos 的 clang)和运行时设置(如 UseCompressedClassPointers)。例如在我的 linux:

  • Java HotSpot(TM) 64 位服务器 VM 1.6。0_45 限制 Integer.MAX_VALUE
  • Java HotSpot(TM) 64 位服务器 VM 1.7。0_72 限制 Integer.MAX_VALUE-1
  • Java HotSpot(TM) 64 位服务器 VM 1.8。0_40 限制 Integer.MAX_VALUE-2

有用的链接

好吧,Ivan 已经正确地指出数组长度确实有一个明确定义的上限,并且它又是 JVM/Platform 相关的。事实上,更重要的是,他还表示,您在代码中实际可以创建多少长度的数组将主要取决于您在执行时为程序分配了多少最大堆space。

我只想添加一小段代码来支持他的解释。例如,理论上,数组 [] 应该接受长度 <= INTEGER.MAX_VALUE - x(这里 x 是 header 大小,再次 JVM/Platform 特定)但是假设你 运行 following Java program with VM option -Xmx32m 然后你会看到创建的数组的 none 达到接近长度MAX_ARRAY_SIZE(即 2147483639)

字节[]数组:1字节
0 l=1048576 s=1mb
1 l=2097152 s=2mb
2 l=4194304 s=4mb
3 l=8388608 s=8mb
java.lang.OutOfMemoryError: Java 堆 space l=16777216 s=16mb

char[] 数组:2 字节
0 l=1048576 s=2mb
1 l=2097152 s=4mb
2 l=4194304 s=8mb
java.lang.OutOfMemoryError: Java 堆 space l=8388608 s=16mb

int[] 数组:4 字节
0 l=1048576 s=4mb
1 l=2097152 s=8mb
java.lang.OutOfMemoryError: Java 堆 space l=4194304 s=16mb

double[] 数组:8 字节
0 l=1048576 s=8mb
java.lang.OutOfMemoryError: Java 堆 space l=2097152 s=16mb

代码如下:

    byte[] barray = null;
    System.out.println("\nbyte[] array : 1 byte");
    try {
        for (ii=0; ii < 32; ii++) {
            barray = new byte[(int)Math.pow(2, ii)*1024*1024];
            System.out.println(ii + " l=" + barray.length + " s=" + barray.length / (1024 * 1024) + "mb");
        }
    }
    catch (Throwable e) {
        barray = null;
        System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + (int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
    }

    char[] carray = null;
    System.out.println("\nchar[] array : 2 byte");
    try {
        for (ii=0; ii < 32; ii++) {
            carray = new char[(int)Math.pow(2, ii)*1024*1024];
            System.out.println(ii + " l=" + carray.length + " s=" + 2*carray.length / (1024 * 1024) + "mb");
        }
    }
    catch (Throwable e) {
        carray = null;
        System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + 2*(int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
    }

    int[] iarray = null;
    System.out.println("\nint[] array : 4 byte");
    try {
        for (ii=0; ii < 32; ii++) {
            iarray = new int[(int)Math.pow(2, ii)*1024*1024];
            System.out.println(ii + " l=" + iarray.length + " s=" + 4*iarray.length / (1024 * 1024) + "mb");
        }
    }
    catch (Throwable e) {
        iarray = null;
        System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + 4*(int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
    }

    double[] darray = null;
    System.out.println("\ndouble[] array : 8 byte");
    try {
        for (ii=0; ii < 32; ii++) {
            darray = new double[(int)Math.pow(2, ii)*1024*1024];
            System.out.println(ii + " l=" + darray.length + " s=" + 8*darray.length / (1024 * 1024) + "mb");
        }
    }
    catch (Throwable e) {
        darray = null;
        System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + 8*(int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
    }

通过转到 cmd 并输入此行找到您的最大堆大小

javaw -XX:+PrintFlagsFinal | find "MaxHeapSize"

然后除以1.5,您将得到适合您计算机的数组的近似最大大小