I was playing with OOM errors today and I found something I can't explain myself.
I try to allocate an array bigger than the heap, expecting a "Requested array size exceeds VM limit" error, but I get a "Java heap space" error instead.
According to the 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."
Code :
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);
}
}
This code works as expected with a heap big enough to store my array :
$ javac MemOverflow.java && java -Xmx800m MemOverflow
Heap max size: 800MB
100000000
However, when I reduce my heap size I get the wrong OOM error :
$ 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
Am I missing something here ? I know I can generate the error I want using Integer.MAX_VALUE
as the array size, but I would like it to be thrown by adjusting the heap size.
Java version :
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)
Edit: Based on @cdalxndr answer, I also tried with the Oracle latest JDK 15 but I get the same result.
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)
Edit 2: I reported it: JDK-8254804.
Edit 3: 2021-01-19 update
The documentation has been fixed. in the v16-ea (early access).
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.
Edit 4 : 2021-03-20 update
The documentation cited, 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).
...is incorrect.
What "requested array size exceeds VM limit" message really means is that there is an limit imposed by the implementation of the JVM. Exceeding this limit will always cause the failure, no matter how much heap space is available. An OutOfMemoryError with this message occurs if an there is an attempt to allocate an array with close to Integer.MAX_VALUE
elements. (This is not a fixed limit. See below.)
By contrast, an OutOfMemoryError with the "Java heap space" message means that the request could have been fulfilled if circumstances were different: for example, if less memory were used by other objects, or if the JVM's maximum heap size were increased.
I've repurposed bug JDK-8254804 to fix the documentation in question.
There is a comment that seems relevant in 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;
Note that this is in library code, not in the Hotspot JVM code. It's the library code's conservative guess for an array size that doesn't exceed the limit that might be imposed by the JVM. The problem is that the JVM's maximum array size limit might vary depending on different circumstances, such as the garbage collector in use, whether the JVM is running in 32-bit or 64-bit mode, or even the JVM (Hotspot or other) upon which it is running. The library needs to be a bit conservative here because it needs to run on different JVMs in different circumstances, and there's no standardized interface to the JVM that returns the "maximum allowed array size." (Indeed, such a concept is potentially ill-defined, as the maximum array size might differ for different array types, or it might change from one moment to the next.)