java.lang.OutOfMemoryError: Requested array size exceeds VM limit

/ Java / 没有评论 / 2558浏览

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

有些人可能写个好多年的代码,但是他不一定遇见过这个错误。java.lang.OutOfMemoryError: Requested array size exceeds VM limit 错误非常的少见,你可以在面试中问问,能说明白这个错误的绝对是认真看过 JVM 的。今天我们就说说 Requested array size exceeds VM limit (创建的数组长度超过限制)。

Java 语言其实是限制了数组的最大长度。这个不是底层 JVM 限制的,而是 Java 语言在创建数组时,就限制了数组的长度最大为 Integer.MAX_VALUE。因为数组的下标就是 int 类型的,你最大也不可能超过 Integer.MAX_VALUE。 java.lang.OutOfMemoryError: Requested array size exceeds VM limit

需要注意的是,并不是所有的操作系统和 JDK 都限制的数组的度最大长为 Integer.MAX_VALUE。比如在 64位的 MB Pro 上,Java 1.7 平台可以分配长度为 2,147,483,645, 以及 Integer.MAX_VALUE-2) 的数组。但差别不是特别的大,基本上都在 1 ~ 21亿 之间的范围。

java.lang.OutOfMemoryError: Requested array size exceeds VM limit 错误是由 JVM 中的本地代码抛出的。

在为数组分配内存之前,JVM 会执行一项检查。要分配的数据结构在该平台是否可以寻址(addressable)。如果不能寻址(addressable)就会抛出这个错误。

下面我们通过一个示例来演示 java.lang.OutOfMemoryError: Requested array size exceeds VM limit 错误。

for (int i = 3; i >= 0; i--) {
  try {
    int[] arr = new int[Integer.MAX_VALUE-i];
    System.out.format("Successfully initialized an array with %,d elements.\n", Integer.MAX_VALUE-i);
  } catch (Throwable t) {
    t.printStackTrace();
  }
}

执行上面的代码,在 64位 Mac OS X 的 Hotspot 7 平台上,就会得到类似下面这样的结果:

java.lang.OutOfMemoryError: Java heap space
  at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)
java.lang.OutOfMemoryError: Java heap space
  at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
  at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
  at eu.plumbr.demo.ArraySize.main(ArraySize.java:8)

前面两次抛出了 Java heap space 错误。这是因为 2^31-1 个 int 数占用的内存超过了JVM默认的8GB堆内存。所以说 Requested array size exceeds VM limit 错误很少被看到。

发生 java.lang.OutOfMemoryError: Requested array size exceeds VM limit 错误的原因很可能就是你的代码问题。

数组太大,最终长度超过平台限制值,但小于 Integer.MAX_VALUE。

解决方法就是检查你的代码中是否有创建超大数组的地方。

如果确实需要处理超大数据集,那就要考虑调整解决方案了。例如拆分成多个小块,按批次加载;或者放弃使用标准库,而是自己处理数据结构,比如使用 sun.misc.Unsafe 类,通过 Unsafe 工具类可以像 C 语言一样直接分配内存。

参考资料