为什么我无法创建大尺寸的数组?

时间:2015-07-13 11:46:41

标签: java jvm jvm-hotspot

为什么不可能创建一个max int size的数组?

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

我找到了这个解释:

  

通过32位整数访问Java数组,最大理论数组大小为2147483647个元素。

但是你可以看到我的代码不起作用。创建大小

的数组也是不可能的
new int[Integer.MAX_VALUE - 5];

技术细节

  • 64位HotSpot JVM
  • OSX 10.10.4

PS

为什么-5实际上?

5 个答案:

答案 0 :(得分:18)

理论

有两种可能的例外情况:

  • OutOfMemoryError: Java heap space表示您的数组不适合java堆空间。为了解决这个问题,可以使用JVM选项-Xmx来增加最大堆大小。另请考虑the maximum size of object cannot be larger than the largest heap generation
  • OutOfMemoryError: Requested array size exceeds VM limit表示超出了特定于平台的大小:
    • 上限是由用于描述数组中索引的大小类型的限制设置的,因此理论数组大小受2^31-1=2147483647元素的限制。
    • 另一个限制是特定于JVM /平台。根据{{​​3}},对数组长度没有严格的限制,因此可以在不违反JLS的情况下减少数组大小。

实践

在HotSpot中,JVM数组的大小受内部表示的限制。在GC代码中,JVM在堆字中传递一个数组大小为int,然后从堆字转换回jint,这可能会导致溢出。因此,为了避免崩溃和意外行为,最大数组长度受chapter 10: Arrays of The Java Language Specification, Java SE 7 Edition的限制。其中(max size - header size)取决于用于构建运行的JVM的C / C ++编译器(用于linux的gcc,用于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

有用的链接

答案 1 :(得分:1)

某些VM会在数组中保留一些标题字。

最大"安全"号码会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;

因此,它取决于您现在的SYSTEM上JVM可用的最大内存

编辑:为何显示OOM。

元素数量= 2 147 483 639

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

元素总内存8589934556 KB == 8.589934555999999 GB

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

所以你需要的不仅仅是由于Overheads而分配的内容,而且应该是连续内存

答案 2 :(得分:1)

仅仅拥有足够的堆来进行分配是不够的;您需要具有足够大小的单个堆区域。如您所知,堆分为几代。

对于8 GB的单个分配,您必须确保单个堆区域(加上一些开销)。使用12 GB的-Xmx,您可能仍然很短。使用其他选项来控制旧代的大小。

答案 3 :(得分:0)

嗯,Ivan已经正确地指出数组长度确实具有明确定义的上限,并且它再次依赖于JVM /平台。实际上,更重要的是,他还说过你可以在代码中实际创建的数组长度主要取决于你在执行时分配给程序的最大堆空间。

我只想添加一些小代码片段来支持他的解释。例如,理论上,array []应该接受长度< = INTEGER.MAX_VALUE - x(这里x是标题大小,同样是JVM / Platform特定的)但是假设您使用VM运行跟随 Java程序选项 -Xmx32m 然后您将看到所创建的数组都没有达到接近MAX_ARRAY_SIZE的长度(即2147483639)

byte []数组: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堆空间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堆空间l = 8388608 s = 16mb

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

double []数组:8字节
0 l = 1048576 s = 8mb
java.lang.OutOfMemoryError:Java堆空间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");
    }

答案 4 :(得分:0)

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

javaw -XX:+PrintFlagsFinal | find "MaxHeapSize"

然后将其除以1.5,您将获得计算机阵列的近似最大大小