Unexpected OutOfMemoryError when allocating an array larger than the heap
Asked Answered
S

3

16

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

JDK 16 is now released with the fixed docs

Slipsheet answered 13/10, 2020 at 16:16 Comment(2)
If you really want to know when theyou get the message Requested array size exceeds VM limit you should just check the OpenJDK sources instead of guessing... github.com/openjdk/jdk/… There is for example a unit test that calls Object[] oa = new Object[Integer.MAX_VALUE]; which causes this error. May be you don't get the error on primitive arrays?Sinapism
@Sinapism I'll take a look at the sources, thanks. If I change my array declaration to new long[Integer.MAX_VALUE]; then I get the expected error. But If my heap is only 100MB then I should have this error when I try to allocate more than 100MB, even if it's not the max int value (well, this is what the documentation says).Slipsheet
N
16

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.)

Nifty answered 19/10, 2020 at 17:30 Comment(1)
Thanks a lot for the details and the issue to fix the docs !Slipsheet
F
4

This may be a jdk implementation difference.

Your docs point to Oracle site, but you are not using Oracle JDK. Your OpenJDK implementation may handle this exception differently, so you don't get Requested array size exceeds VM limit exception.

Some info from my zulu-11 jdk ArrayList.java implementation:

/**
 * The maximum size of array to allocate (unless necessary).
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * OutOfMemoryError: Requested array size exceeds VM limit
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
Fief answered 13/10, 2020 at 17:15 Comment(1)
Thanks for the tip, I downloaded the latest Oracle JDK (15) but I have the same result (I edited my answer).Slipsheet
J
0

I was pushing JVM arrays really hard with a 2G- array of long, and to make it work, I had to reduce the size just a bit: 0x7fffffff fails but 0x7ffff000 works, assuming you have the heap space for it. Apparently you can fly close to the sun, but you cannot touch it. :D

I find it a bit amazing that they array index was not long from the start! Java seems at a disadvantage in a world of huge data. I am not mourning the loss of unsigned, it is just one bit, and 2^63- is big enough! Big data handlers like big hash maps, and the heart of hash is the bucket array.

Juttajutty answered 4/7, 2024 at 18:5 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.