Fixing Java OutOfMemoryError: Java heap space
This error occurs when the JVM runs out of heap memory. Increase heap size with -Xmx, optimize code, and monitor usage to prevent recurrence.
Symptoms
When a Java application runs out of heap memory, it throws java.lang.OutOfMemoryError: Java heap space. Common symptoms include:
- Application crashes or freezes unexpectedly.
- Error logs showing
OutOfMemoryErrorwith stack traces. - Slow performance followed by abrupt termination.
- In web servers (e.g., Tomcat), requests fail with 500 errors.
Root Causes
The Java heap is where objects are allocated. The error appears when the JVM cannot allocate an object due to insufficient heap space. Primary causes include:
- Insufficient heap size: Default heap (e.g., 256MB) is too small for the application's workload.
- Memory leaks: Objects are unintentionally held in memory (e.g., forgotten references, static collections, listeners not removed).
- High data volume: Processing large files, datasets, or many concurrent users without increasing heap.
- Inefficient data structures: Using memory-heavy structures like
ArrayListfor huge datasets. - Fragmentation: Frequent allocation/deallocation of objects of different sizes can fragment the heap.
Step-by-Step Fix
1. Increase JVM Heap Size
Set the maximum heap size using the -Xmx flag. For example, to allow 2GB heap:
java -Xmx2g -jar myapp.jarAlso set initial heap (-Xms) to the same value to avoid resizing overhead:
java -Xms2g -Xmx2g -jar myapp.jarFor Tomcat, edit CATALINA_OPTS in setenv.sh or setenv.bat:
export CATALINA_OPTS="-Xms2g -Xmx2g"For Eclipse/IntelliJ, set in run configuration VM arguments.
2. Identify Memory Leaks
Use heap dump analysis. Generate a heap dump when OOM occurs:
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump.hprof -Xmx2g -jar myapp.jarAnalyze with tools like Eclipse MAT or VisualVM. Look for:
- Large object graphs held by static fields.
- ThreadLocal variables not cleaned up.
- Unclosed resources (streams, connections).
- Listener/callback registrations never removed.
3. Optimize Code
- Use appropriate data structures: prefer
ArrayListoverLinkedListfor random access. - Process data in streams instead of loading everything into memory.
- Set collection initial capacities to avoid resizing.
- Clear references explicitly when done (e.g.,
list.clear()).
4. Monitor and Tune Garbage Collection
Enable GC logging:
-Xlog:gc*:file=gc.logAnalyze GC logs to see if GC is running too frequently or not reclaiming memory. Consider switching GC algorithm (e.g., G1GC for large heaps):
-XX:+UseG1GC5. Use Profiling Tools
Run your application under load with a profiler (VisualVM, JProfiler) to identify memory-hungry methods or objects.
Alternative Fixes
- Reduce object size: Use primitive types instead of wrapper classes, use
StringBuilderinstead of string concatenation. - Increase swap space (temporary, not recommended for production).
- Distribute load: Use multiple JVM instances or microservices.
- Use off-heap storage: For caches, use libraries like Ehcache or MapDB that store data outside the heap.
Prevention
- Set appropriate heap size based on load testing.
- Implement code reviews focusing on memory management.
- Use static analysis tools (FindBugs, SonarQube) to detect potential leaks.
- Monitor heap usage with JMX or APM tools (New Relic, Datadog).
- Set up alerts for high heap usage (e.g., >80% of max).
- Regularly review and clean up unused references.
Example: Fixing a Memory Leak
Suppose you have a cache implemented as a static HashMap that grows indefinitely. Fix by using WeakHashMap or a bounded cache (e.g., Guava Cache):
// Before: static Map cache = new HashMap<>();
// After: CacheBuilder.newBuilder().maximumSize(1000).build() This prevents unbounded growth and avoids OOM.
Was this solution helpful?