Solving java.lang.OutOfMemoryError: java heap space
This article goes through the most common Java OutOfMemory Error, which happens when you saturate the Java Heap Memory. Within this article, we will show how to fix this error depending on whether you are an application user, a DevOps engineer, or an application developer.
The Java Heap Space is the area of memory where the Java objects reside. When a Java program executes, the JVM (Java Virtual Machine) allocates some initial memory to the heap. If the JVM cannot allocate enough memory for a new object, and the garbage collector cannot reclaim sufficient space, the JVM will throw a java.lang.OutOfMemoryError: Java heap space exception.
In general terms, there are distinct ways to approach this problem depending on your role.
Fixing Heap Shortage as an Application User
If you are an application end-user, you have no control over the source code. Besides reporting the issue in the appropriate channel, the most common solution is to increase the heap size using the -Xmx (maximum) and -Xms (initial) JVM options. For example, to increase the maximum heap size to 2048 MB, you can use the following option:
java -Xms2048m -Xmx2048m -jar application.jar
You will need to apply these JVM settings in your application launcher. For instance, in heavy Java desktop applications like large modded Minecraft clients, 2 GB is no longer sufficient; modern configurations often require 8 to 10 GB of allocated RAM using -Xmx10G. Likewise, if you need to allocate more memory to a developer tool like Eclipse, you will need to modify the -Xmx value directly inside the eclipse.ini file.
Fixing Heap Shortage in Containerized Environments (Docker/Kubernetes)
If you are running Java inside a Docker container, simply hardcoding -Xmx can be problematic. Historically, older JVMs were not completely cgroup-aware and would calculate heap sizes based on the host machine's total RAM, frequently resulting in OS-level OOM kills.
Modern JVMs (Java 10+ and backported to Java 8u191+) feature a flag called -XX:+UseContainerSupport which is enabled by default. Instead of hardcoding -Xmx, it is a best practice to use dynamic RAM percentages so the heap scales safely with your container limits:
java -XX:MaxRAMPercentage=75.0 -jar application.jar
This securely allocates 75% of the container's available memory to the Java heap, leaving the remaining 25% for native memory and OS overhead.
Fixing Heap Shortage as an Application Developer
If you are an application developer and setting a higher -Xmx merely delays the inevitable crash, you are likely dealing with a memory leak or an architectural bottleneck.
Application Memory Leaks and Code Pitfalls
Memory leaks in Java occur when objects that are no longer needed are unintentionally kept alive by strong references, completely preventing garbage collection. Symptoms include continuous memory growth over time, a slow or unresponsive application, and finally, the OutOfMemoryError. A major source of leaks in enterprise software is the improper use of collections (like HashMap or ArrayList), where containers hold onto stale element references indefinitely.
Another classic trigger is loading massive datasets directly into memory all at once. For example, reading a huge file directly into a byte array will quickly crash your heap:
// Anti-pattern: Loads entire file into memory
byte[] data = Files.readAllBytes(Paths.get("largefile.txt"));
To solve this, process data in smaller chunks using streams or a BufferedReader:
try (BufferedReader reader = new BufferedReader(new FileReader("largefile.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
Diagnosing with Heap Dumps and Eclipse MAT
To diagnose a persistent leak, you must capture a heap dump. A best practice for production environments is to start your JVM with this life-saving flag: -XX:+HeapDumpOnOutOfMemoryError. This automatically generates a .hprof snapshot when the JVM crashes.
Next, analyze the dump with tools like JVisualVM or Eclipse Memory Analyzer (MAT). When you open a heap dump, Eclipse MAT is excellent for analyzing Leak Suspects and identifying the Top Consumers in your JVM. You can dig deeper into the incoming and outgoing references to see exactly which objects are retaining memory. For example, in a WildFly environment, you might observe unbounded growth in HTTP session managers like io.undertow.server.session.InMemorySessionManager.
Garbage Collection Tuning & The "GC Overhead Limit Exceeded" Error
Sometimes, the heap isn't strictly leaking, but the Garbage Collector is severely overburdened. You might encounter a specific error variant: java.lang.OutOfMemoryError: GC Overhead Limit Exceeded. By default, the parallel collector throws this if the JVM spends more than 98% of its CPU time performing garbage collection and recovers less than 2% of the heap space.
Modernizing your GC settings is critical. Always stay on a recent JVM version, as newer releases include vast optimizations that enhance GC performance.
- Since Java 9, G1GC is the default garbage collector, optimized for larger heaps and predictable pause times.
- For ultra-low latency without pause-time concerns tied to heap size, modern Java offers the Z Garbage Collector (ZGC) (
-XX:+UseZGC), which scales seamlessly for heap sizes from a few hundred megabytes up to 16TB.
You can also influence the JVM's heap sizing heuristics:
-XX:MinHeapFreeRatio=40sets the minimum percentage of the heap that must remain free after a GC cycle, keeping space ready for new objects.-XX:MaxHeapFreeRatio=70dictates the maximum free percentage before the GC aggressively shrinks the heap to return memory to the OS.
Finalizer Thread Under Pressure (And Modern Alternatives)
Another possible cause of a heap shortage relates to the finalizer thread. Historically, when an object implementing finalize() became unreachable, it was placed in a finalization queue. If the finalizer thread could not process this queue fast enough (e.g., due to high-priority application threads starving it of CPU), the heap would fill up with pending objects and crash.
Modernization Note: Finalization has been officially deprecated since JDK 9 and is targeted for removal (JEP 421). Do not rely on it. To ensure proper cleanup of resources without memory penalties, rely on the try-with-resources statement. If background post-mortem cleanup is strictly necessary, migrate to the modern Cleaner API (java.lang.ref.Cleaner), which avoids the severe performance and resurrection risks of traditional finalizers.
Conclusion
This article walked through the possible causes of a Java OutOfMemoryError due to a shortage of Heap Memory. If adjusting -Xmx or container limits does not resolve the issue, you will need to capture a Heap Dump and initiate a code-level analysis to hunt down memory leaks or optimize your resource processing architecture.
Recommended Articles
Fix Java.lang.OutOfMemoryError: Compressed Class Space Error on 64-bit Platforms
Learn how to resolve 'java.lang.OutOfMemoryError: Compressed class space error' in Java 1.8 and later versions.
Fixing HTTP 415 'Unsupported Media Type' Error: A Comprehensive Guide for Developers
Learn how to resolve HTTP 415 errors in your Java applications. Fix common causes and improve API reliability.
Mastering OutOfMemoryError: Direct Buffer Memory in Java - A Comprehensive Guide
Learn how to resolve OutOfMemoryError: Direct buffer memory issues in your Java applications. #Java #WildFly #DirectByteBuffer
Fix Java Metaspace Errors with Elastic Metaspace (JEP 387) in Java 17
Fix java.lang.OutOfMemoryError: Metaspace in Java: root causes, JVM flags, jcmd/jconsole/NMT diagnostics, a reproducer, and how to correctly size Metaspace on Kubernetes/OpenShift container images.