OutOfMemoryError Types and Causes

Naveen AnamaneniUpdated · 5 min read

OutOfMemoryError (OOM) is thrown when the JVM can't get memory it needs. It is an Error, not an Exception, because the program usually can't recover from it. The most useful part is its message, which tells you which memory area ran out: the heap, metaspace, direct buffers, or native memory for threads. Each has different causes and different fixes. In this lesson, you will see the real messages for each type and learn what to do about them.

The Types of OutOfMemoryError

MessageAreaCommon causesFirst fix to try
Java heap spaceHeapA memory leak, or the heap really is too smallFind the leak with a heap dump; then raise -Xmx if needed
GC overhead limit exceededHeap (Parallel GC)Almost all time spent in GC, freeing almost nothingSame as heap space
MetaspaceMetaspaceToo many classes, often from repeatedly creating class loadersFix class loader leaks; raise -XX:MaxMetaspaceSize
Requested array size exceeds VM limitHeapAn array larger than the JVM allowsFix the size calculation
Cannot reserve ... bytes of direct buffer memoryDirect memoryMany ByteBuffer.allocateDirect buffersFree or reuse buffers; raise -XX:MaxDirectMemorySize
unable to create native threadNative memory, OS limitsToo many platform threadsUse a thread pool or virtual threads

Example: Running Out of Heap Safely

Let's see an example that fills a small amount of heap on purpose, catches the error, and recovers by dropping the data. The program limits itself with a list it can clear, so it doesn't depend on any JVM option.

HeapOom.java▶ Try it
import java.util.ArrayList;
import java.util.List;
 
public class HeapOom {
    public static void main(String[] args) {
        List<long[]> hog = new ArrayList<>();
        try {
            while (true) {
                hog.add(new long[10_000_000]);
            }
        } catch (OutOfMemoryError e) {
            int blocks = hog.size();
            hog.clear();
            System.out.println("Caught: " + e.getMessage());
            System.out.println("Filled at least one 80 MB block? "
                + (blocks >= 1));
        }
        System.out.println("Program continues after freeing the memory");
    }
}
Output
Caught: Java heap space
Filled at least one 80 MB block? true
Program continues after freeing the memory

Explanation: Each long[10_000_000] is 80 MB. The loop keeps them all reachable until the heap is full, and the next allocation throws OutOfMemoryError: Java heap space. Here the program recovers because all the memory belongs to one list it can clear, but in real programs an OOM usually leaves things in an unknown state.

The Other Messages, from Real Runs

These outputs come from a test program run with small limits on JDK 25:

Terminal
java -Xmx64m Oom array
java -XX:MaxDirectMemorySize=16m Oom direct
java -XX:MaxMetaspaceSize=16m Oom metaspace
Terminal output
java.lang.OutOfMemoryError: Requested array size exceeds VM limit
java.lang.OutOfMemoryError: Cannot reserve 1000000 bytes of direct buffer memory (allocated: 16000000, limit: 16777216)
java.lang.OutOfMemoryError: Metaspace

Explanation: new int[Integer.MAX_VALUE] asks for an array bigger than the JVM supports. Allocating 1 MB direct buffers in a loop hits the 16 MB direct-memory limit, and the message even reports how much was allocated. Loading the same class with thousands of new class loaders fills metaspace, because each loader gets its own copy of the class.

What to Do When It Happens

  1. Keep the evidence: run with -XX:+HeapDumpOnOutOfMemoryError. With it, the JVM prints, for example, Dumping heap to /var/dumps/java_pid1234.hprof ... and saves the dump.
  2. Check GC logs: a steadily rising "after" heap value points to a leak.
  3. Analyze the dump in Eclipse MAT or VisualVM (see "Memory Leaks in Java").
  4. Only then change sizes such as -Xmx or -XX:MaxMetaspaceSize.
  5. In production, consider -XX:+ExitOnOutOfMemoryError so a broken JVM restarts instead of limping on.

Common Mistakes

Raising -Xmx without looking for a leak. A leak just takes longer to crash.

Treating every OOM as a heap problem. Metaspace, direct memory and threads fail differently.

Catching Throwable and ignoring errors. OOM and other Errors should usually stop the program.

Interview Corner

What are the different types of OutOfMemoryError?

The message identifies the exhausted resource: "Java heap space" (heap full, often a leak), "GC overhead limit exceeded" (Parallel GC spending nearly all time collecting while reclaiming little), "Metaspace" (too much class metadata, often a class loader leak), "Requested array size exceeds VM limit", "Cannot reserve ... bytes of direct buffer memory" (off-heap ByteBuffers), and "unable to create native thread" (OS thread or native memory limits). Diagnose with GC logs, heap dumps captured via -XX:+HeapDumpOnOutOfMemoryError, and native memory tracking, then fix the cause before adjusting sizes.

Practice

  1. Easy: Change HeapOom to allocate byte[1_000_000] blocks and print how many it managed before the error.
  2. Harder: Write a program that starts platform threads that sleep forever until it fails, and catch the OutOfMemoryError message. Then rewrite it with virtual threads and compare.

Quick check: OutOfMemoryError

4 questions
1Which message means the heap is full?
2What usually fills metaspace?
3Is OutOfMemoryError an Exception?
4What should you do before raising -Xmx?

Key Takeaways

Was this tutorial helpful?

Lesson 451 of 482