java - 分配大于堆的数组时出现意外的 OutOfMemoryError

标签 java out-of-memory heap-memory java-11

今天我在玩OOM错误,我发现了一些我自己无法解释的东西。
我尝试分配一个比堆大的数组,期望 “请求的阵列大小超出 VM 限制”错误,但我得到一个“ Java 堆空间 ”错误。
根据JDK 11 documentation "3 Troubleshoot Memory Leaks > Understand the OutOfMemoryError Exception" :

Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit

Cause: The detail message "Requested array size exceeds VM limit" indicates that the application (or APIs used by that application) attempted to allocate an array that is largerthan the heap size. For example, if an application attempts to allocate an array of 512 MB, but the maximum heap size is 256 MB, then OutOfMemoryError will be thrown with the reason “Requested array size exceeds VM limit."



代码 :
public class MemOverflow {

    public static void main(final String[] args) {
        System.out.println("Heap max size: " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + "MB");
        long[] array = new long[100_000_000]; // ~800MB
        System.out.println(array.length);
    }
}
此代码按预期工作,堆足够大以存储我的数组:
$ javac MemOverflow.java && java -Xmx800m MemOverflow
Heap max size: 800MB
100000000
但是,当我减小堆大小时,会出现错误的 OOM 错误:
$ javac MemOverflow.java && java -Xmx100m MemOverflow
Heap max size: 100MB
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at MemOverflow.main(MemOverflow.java:5)
# I'm expected the "Requested array size exceeds VM limit" error here
我在这里错过了什么吗? 我知道我可以使用 Integer.MAX_VALUE 生成我想要的错误作为数组大小,但我希望通过调整堆大小来抛出它。
Java版本:
openjdk version "11.0.8" 2020-07-14
OpenJDK Runtime Environment (build 11.0.8+10-post-Ubuntu-0ubuntu120.04)
OpenJDK 64-Bit Server VM (build 11.0.8+10-post-Ubuntu-0ubuntu120.04, mixed mode)
编辑:
根据@cdalxndr 的回答,我也尝试使用 Oracle 最新的 JDK 15,但我得到了相同的结果。
java version "15" 2020-09-15
Java(TM) SE Runtime Environment (build 15+36-1562)
Java HotSpot(TM) 64-Bit Server VM (build 15+36-1562, mixed mode, sharing)
编辑 2:
我举报了:JDK-8254804 .

编辑 3:2021-01-19 更新
The documentation has been fixed.在 v16-ea(早期访问)中。

Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit

Cause: The detail message "Requested array size exceeds VM limit" indicates that the application (or APIs used by that application) attempted to allocate an array with a size larger than the JVM implementation limit, irrespective of how much heap size is available.



编辑 4:2021-03-20 更新
JDK 16 is now released with the fixed docs

最佳答案

引用的文档 Understand the OutOfMemoryException ,

Exception in thread thread_name: java.lang.OutOfMemoryError: Requested array size exceeds VM limit

Cause: The detail message "Requested array size exceeds VM limit" indicates that the application (or APIs used by that application) attempted to allocate an array that is larger than the heap size. For example, if an application attempts to allocate an array of 512 MB, but the maximum heap size is 256 MB, then OutOfMemoryError will be thrown with the reason “Requested array size exceeds VM limit."

Action: Usually the problem is either a configuration issue (heap size too small) or a bug that results in an application attempting to create a huge array (for example, when the number of elements in the array is computed using an algorithm that computes an incorrect size).


...是不正确的。
“请求的数组大小超过 VM 限制”消息的真正含义是 JVM 的实现施加了限制。超过这个限制总是会导致失败,不管有多少可用的堆空间。如果尝试分配接近 Integer.MAX_VALUE 的数组,则会出现带有此消息的 OutOfMemoryError。元素。 (这不是一个固定的限制。见下文。)
相比之下,带有“Java 堆空间”消息的 OutOfMemoryError 意味着如果情况不同,请求可能已经完成:例如,如果其他对象使用的内存更少,或者 JVM 的最大堆大小增加了。
我重新利用了错误 JDK-8254804修复有问题的文档。
ArraysSupport.java 中有一条似乎相关的评论:
/**
 * The maximum length of array to allocate (unless necessary).
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * {@code OutOfMemoryError: Requested array size exceeds VM limit}
 */
public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
请注意,这是在库代码中,而不是在 Hotspot JVM 代码中。这是库代码对不超过 JVM 可能施加的限制的数组大小的保守猜测。问题是 JVM 的最大数组大小限制可能会根据不同的情况而有所不同,例如使用的垃圾收集器,JVM 是在 32 位还是 64 位模式下运行,甚至是 JVM(Hotspot 或其他)它正在运行。这个库在这里需要有点保守,因为它需要在不同的情况下在不同的 JVM 上运行,并且没有返回“最大允许数组大小”的 JVM 的标准化接口(interface)。 (实际上,这样的概念可能定义不明确,因为不同数组类型的最大数组大小可能不同,或者它可能从一个时刻到下一个时刻发生变化。)

关于java - 分配大于堆的数组时出现意外的 OutOfMemoryError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64339159/

相关文章:

c# - 为什么编译器为 "yield"生成的枚举器不是结构?

java - LayoutInflater.inflate() 返回的 View 也总是 ViewGroup 吗?

java - 当我要求用户输入时,我无法从数组中删除元素

c - 关于PIC18F25k50中的内存

android - 处理: com.android.packageinstaller OutOfMemoryError

c++ - 堆分配对象的 setter

c++ - 堆栈 VS 堆与类

java - 找到多个 BitSets java 的共同父级

java - Wicket 中的不同页面有多个超时期限吗?

java - 使用 JSON Simple 解析大型 JSON 文件(OutOfMemoryError)