OutOfMemoryError 与 NegativeArraySizeException

OutOfMemoryError vs NegativeArraySizeException

当我使用 Integer.MAX_VALUE 初始化 StringBuffer 构造函数时,它抛出 OutOfMemoryError 并且当我向它添加 16 时它抛出 NegativeArraySizeException

  public class Test {
        public static void main(String[] arg) {
            StringBuffer sb = new StringBuffer(Integer.MAX_VALUE + 16);
            sb.append("A");
        }
    }

有人可以帮助我理解这种行为吗?

如果您将 1 添加到 Integer.MAX_VALUE,您将得到 Integer.MIN_VALUE。从这里开始,您应该知道添加 16:

时会发生什么
Integer.MAX_VALUE + 16 == Integer.MAX_VALUE + 1 + 15 == Integer.MIN_VALUE + 15

说明

Java 中的整数由 32 位表示,而最高有效位(最左边的位)表示 整数的符号(0 == 正,1 == 负)。

Integer.MAX_VALUE表示为:01111111111111111111111111111111。添加 1 会将所有 1 翻转为 0,并将最左边的 0 翻转为 1,结果是 10000000000000000000000000000000,这是一个负数, 更具体 Integer.MIN_VALUE.

A StringBuffer 在其实现中使用字符数组来存储字符串。这是它的超级 class、AbstractStringBuilder 的构造函数,其中实际分配了数组:

AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

如您所见,新的 char[] 数组使用您传入的容量整数进行初始化。Java 中字符数组的最大大小是 更小的[=堆大小的 27=] 或 Integer.MAX_VALUE。如果以下代码抛出错误,那么我的猜测是您的堆 运行 内存不足:

StringBuffer sb = new StringBuffer(Integer.MAX_VALUE);

假设堆有足够的 space,我本以为这会起作用。但是尝试分配大小为 Integer.MAX_VALUE + 16 StringBuffer 无法 工作,因为它会超过基础 char[] 数组的最大大小。

您在这里实际上问了两个完全不同的问题,因此必须分开解决。

1.为什么 new StringBuffer(Integer.MAX_VALUE) 抛出 OutOfMemoryError

StringBuffer 构造函数尝试实例化 char 的数组,其大小是您传递的值。因此,您正在隐式地尝试实例化一个大小为 Integer.MAX_VALUE 的数组。实例化数组可能会得到 OutOfMemoryError 的原因有两个。

第一个原因是您确实没有足够的堆space。在不知道您的程序中还发生了什么,或者您的堆设置是什么的情况下,我无法判断您是否正在发生这种情况。但是您可以在启动时使用 -Xmx 选项为您的 JVM 选择最大堆大小。显然,假设您的计算机有足够的 RAM,您需要将其设置为几千兆字节才能使其正常工作(例如 -Xmx8g)。

实例化数组时出现 OutOfMemoryError 的第二个原因是您超出了最大数组大小。这肯定发生在这里。最大数组大小未由 Java 语言规范定义,并且因 JVM 的不同而不同。在大多数 modern JVM 中,最大数组大小为 Integer.MAX_VALUE - 2,但有些 JVM 中的最大值为 Integer.MAX_VALUE - 5Integer.MAX_VALUE - 8。无论如何,Integer.MAX_VALUE肯定是超过了限制。

2。为什么 new StringBuffer(Integer.MAX_VALUE + 16) 会抛出一个 NegativeArraySizeException

这与 Java 中的整数运算有关。任何 int 值都必须介于 Integer.MIN_VALUEInteger.MAX_VALUE 之间。此外,当您添加两个 int 值时,Java 语言规范保证答案是 int. Therefore, when you evaluateInteger.MAX_VALUE + 16`,您不会得到数学上正确的答案。其实Java所做的就是工作mod232。另一种看待它的方式是,JVM 将加上或减去 232 的倍数,以使答案在正确的范围内。

这意味着Integer.MAX_VALUE + 16的值实际上在数值上等于Integer.MAX_VALUE + 16 -232,这是一个很大的负数。正如我前面提到的,StringBuffer 构造函数尝试实例化一个数组,其大小是您传递的值。这种实例化负大小数组的尝试为您提供了 NegativeArraySizeException.