OutOfMemoryError Types and Causes
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
| Message | Area | Common causes | First fix to try |
|---|---|---|---|
Java heap space | Heap | A memory leak, or the heap really is too small | Find the leak with a heap dump; then raise -Xmx if needed |
GC overhead limit exceeded | Heap (Parallel GC) | Almost all time spent in GC, freeing almost nothing | Same as heap space |
Metaspace | Metaspace | Too many classes, often from repeatedly creating class loaders | Fix class loader leaks; raise -XX:MaxMetaspaceSize |
Requested array size exceeds VM limit | Heap | An array larger than the JVM allows | Fix the size calculation |
Cannot reserve ... bytes of direct buffer memory | Direct memory | Many ByteBuffer.allocateDirect buffers | Free or reuse buffers; raise -XX:MaxDirectMemorySize |
unable to create native thread | Native memory, OS limits | Too many platform threads | Use 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.
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");
}
}Caught: Java heap space
Filled at least one 80 MB block? true
Program continues after freeing the memoryExplanation: 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:
java -Xmx64m Oom array
java -XX:MaxDirectMemorySize=16m Oom direct
java -XX:MaxMetaspaceSize=16m Oom metaspacejava.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: MetaspaceExplanation: 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
- 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. - Check GC logs: a steadily rising "after" heap value points to a leak.
- Analyze the dump in Eclipse MAT or VisualVM (see "Memory Leaks in Java").
- Only then change sizes such as
-Xmxor-XX:MaxMetaspaceSize. - In production, consider
-XX:+ExitOnOutOfMemoryErrorso 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
- Easy: Change
HeapOomto allocatebyte[1_000_000]blocks and print how many it managed before the error. - Harder: Write a program that starts platform threads that sleep forever until it fails, and catch the
OutOfMemoryErrormessage. Then rewrite it with virtual threads and compare.
Key Takeaways
Was this tutorial helpful?