【问题标题】:Why I can't create an array with large size?为什么我不能创建一个大尺寸的数组?
【发布时间】:2015-10-01 16:01:56
【问题描述】:

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

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

我找到了这个解释:

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

但正如您所见,我的代码不起作用。也无法创建一个有大小的数组

new int[Integer.MAX_VALUE - 5];

技术细节

  • 64 位 HotSpot JVM
  • OSX 10.10.4

PS

为什么是-5

【问题讨论】:

  • 您的阵列是否有所需的 8 GB 内存?
  • 如您的摘录所示,这是最大的理论限制。另外,您说“不起作用”;有什么错误?我会赌 OOM
  • @Kayaman 是的,我使用 VM 标志 -Xmx12g。
  • @fge 是的,我得到了 OOM。

标签: java jvm jvm-hotspot


【解决方案1】:

理论

有两种可能的例外:

练习

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

有用的链接

【讨论】:

  • 但我只能在 64 位服务器 VM 1.7.0_72 上使用数组大小​​的 Integer.MAX_VALUE-5。所以限制不是 Integer.MAX_VALUE-1 对吧?
  • @JohnWinter 这是因为您在另一个操作系统上运行 JVM。此限制因平台和 JVM 版本而异
  • 问题是为什么任何人都应该将长度(以数组元素为单位)添加到标头大小(以字节为单位)。单位的混合没有丝毫意义。例如,正如您所写,64 位服务器 VM 1.8.0_40 将 int[] 的大小限制为 Integer.MAX_VALUE-2。那么,当我们知道它的对象头具有至少四个整数的大小(64 位标记字、32 位或 64 位类字(取决于是否压缩)、32 位数组长度)时,这有什么好处呢? 32 位字的对象大小仍然大于jint 的最大值,以字节为单位的对象大小已经远远超出了。
  • 是否有任何公开的 API 可以查询此限制? System.getMaxArraySize() 之类的东西?
【解决方案2】:

一些虚拟机在数组中保留一些标题字。

最大“安全”数为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 NOW 上 JVM 可用的最大内存

编辑:为什么显示 OOM。

元素数 = 2 147 483 639

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

仅 Element 的总内存 8589934556 KB == 8.589934555999999 GB

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

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

【讨论】:

  • 即使使用new int[Integer.MAX_VALUE - 8];,我仍然会收到OOM错误。
  • 你的内存大小是多少?
  • 我可以确认 MAX_VALUE - 8 对我有用:OpenJDK Runtime Environment (build 1.8.0_45-b14) / OpenJDK 64-Bit Server VM (build 25.45-b02, mixed mode)。使用-Xmx12g。 (虽然我那可怜的 8G RAM 小机器花了几分钟才从滥用中恢复过来!)
  • 可能是因为你需要对象头 .. 虽然我不确定工程师为什么这样设计它
【解决方案3】:

仅仅有足够的堆用于分配是不够的;您需要有一个足够大的堆 region。如您所知,堆分为几代。

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

【讨论】:

  • 不错,但为什么需要单堆区域? Java 堆内存不具有传染性,那么为什么在这种情况下 JVM 会尝试进行传染性分配呢?
  • 数组总是由 HotSpot 分配在连续的块中。
  • 嗯...我将堆大小设置为-Xms12g -Xmx12g(保留的最小值和最大值),现在它可以工作了。
  • @MarkoTopolnik 哦,我明白了。谢谢。是只有 HotSpot 的情况,还是所有 JVM 实现都必须对数组进行传染分配的 JVM?是否还有其他强制传染性分配的情况?您能否提供一些参考资料,我可以在哪里阅读此信息?
  • 规范没有强制要求,但可能所有的 OpenJdk 衍生产品都会这样做。否则,每次访问都会对性能造成重大影响。
【解决方案4】:

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

我只想添加小代码sn-p来支持他的解释。例如,理论上,数组 [] 应该接受长度 以下 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");
    }

【讨论】:

    【解决方案5】:

    通过 cmd 找到你的最大堆大小并输入这一行

    javaw -XX:+PrintFlagsFinal | find "MaxHeapSize"
    

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

    【讨论】:

      猜你喜欢
      • 2011-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-18
      • 2012-07-23
      • 1970-01-01
      • 2016-06-20
      相关资源
      最近更新 更多