理解 OutOfMemoryError 中给出的数字:所需的数组长度

问题描述 投票:0回答:2

我们正在运行第三方提供的Java程序,该程序从数据库读取数据并对其进行处理。但是,执行失败并出现错误:

java.lang.OutOfMemoryError: Required array length 1313719368 + 1313719368 is too large
    at java.base/jdk.internal.util.ArraysSupport.hugeLength(ArraysSupport.java:649)
    at java.base/jdk.internal.util.ArraysSupport.newLength(ArraysSupport.java:642)
    at java.base/java.util.ArrayList.grow(ArrayList.java:234)
    at java.base/java.util.ArrayList.addAll(ArrayList.java:678)

1313719368 + 1313719368
中的数字代表什么?这是否意味着它尝试分配 2,627,438,736 个元素,跨越某种最大数组长度,然后失败?如果是这样,什么决定了最大数组长度?

java arrays out-of-memory java-11
2个回答
3
投票

这个

OutOfMemoryError
是由内部 JDK 类抛出的
jdk.internal.util.ArraysSupport

    private static int hugeLength(int oldLength, int minGrowth) {
        int minLength = oldLength + minGrowth;
        if (minLength < 0) { // overflow
            throw new OutOfMemoryError(
                "Required array length " + oldLength + " + " + minGrowth + " is too large");
        } else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
            return SOFT_MAX_ARRAY_LENGTH;
        } else {
            return minLength;
        }
    }

此方法由公共 JDK 内部方法

ArraysSupport.newLength
调用,该方法在其 Javadoc 注释中解释了其目的和此异常行为:

给定数组的当前长度、最小增长量和首选增长量,计算新的数组长度。计算以溢出安全的方式完成。

此方法由包含数组的对象使用,该数组可能需要增长以满足某些即时需求(最小增长量),但也希望请求更多空间(首选增长量)以适应未来潜在的需求需要。

[…]

如果所需的最小长度超过 Integer.MAX_VALUE,则此方法会抛出 OutOfMemoryError。

在我的 IDE 中针对 Java 21 库进行搜索,发现 JDK 中的许多不同位置都调用

ArraysSupport.newLength
,包括
ArrayList
FileInputStream
AbstractStringBuilder
。在您的情况下,堆栈跟踪告诉我们这是通过其内部私有方法 ArrayList.addAll
ArrayList.grow
 内发生的。

这告诉我,第三方程序正在执行一些操作,导致

ArrayList
类需要分配一个大于Java支持的最大数组长度的数组。堆栈跟踪的其余部分可能会显示第三方的哪个部分触发了导致错误的 JDK 调用。

要回答您的问题,即它是否“尝试分配 2,627,438,736 个元素”并由于超过最大数组长度而失败,这与此处发生的情况很接近。更准确地说,代码检查给定总长度的数组是否可以合法创建,或者是否会超过最大合法数组长度 (

Integer.MAX_VALUE
)。如果数组长度太长,则会失败,而不是尝试创建无效长度的数组。特别是,第三方代码尝试将项目添加到
ArrayList
,这会增加其大小超过
Integer.MAX_VALUE


0
投票

是的,我认为这就是这个意思。它尝试分配 2,627,438,736 个元素,这远远超过了 Java 数组中允许的最大值 2,147,483,647。

© www.soinside.com 2019 - 2024. All rights reserved.