Java tutorials > Java Virtual Machine (JVM) > Memory Management and Garbage Collection > Common causes of `OutOfMemoryError`?
Common causes of `OutOfMemoryError`?
The OutOfMemoryError
(OOM) in Java is a runtime error thrown when the Java Virtual Machine (JVM) cannot allocate memory for new objects because it is out of free memory, and the garbage collector cannot make more memory available. Understanding the common causes of this error is crucial for writing robust and performant Java applications. This tutorial will explore these causes with code snippets and explanations.
Heap Space Exhaustion
Explanation: This is the most common cause. The Java heap is where objects are allocated. If your application creates objects faster than the garbage collector can reclaim them, the heap will eventually fill up, leading to an OutOfMemoryError
. In the code snippet above, an ArrayList
continuously adds new Object
instances. Without any mechanism to remove these objects, the heap will quickly exhaust its allocated memory.
import java.util.ArrayList;
import java.util.List;
public class HeapOOM {
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
while (true) {
list.add(new Object());
}
}
}
Concepts behind the snippet
This snippet demonstrates the simplest form of heap exhaustion. The key concept is that objects in Java consume memory. If objects are created and not properly released (dereferenced so the garbage collector can reclaim them), memory usage grows. When the JVM's heap space, which is configured by the -Xms
(initial heap size) and -Xmx
(maximum heap size) JVM options, is exceeded, an OutOfMemoryError: Java heap space
error is thrown.
Real-Life Use Case Section
Consider a web application processing large image files. If the application reads these images into memory without properly scaling or releasing the image data after processing, it could quickly consume heap space. Similarly, a data processing application that loads large datasets into memory without using efficient streaming or paging techniques is prone to heap exhaustion.
Best Practices
To prevent heap exhaustion, follow these best practices:
-Xms
and -Xmx
JVM options to allocate an appropriate heap size for your application's needs.StringBuilder
for string concatenation instead of repeatedly creating new String
objects.
Interview Tip
When discussing OutOfMemoryError
in an interview, emphasize your understanding of garbage collection, heap memory, and the importance of profiling and memory management techniques. Be prepared to discuss specific tools you have used for memory analysis and debugging, such as VisualVM or JConsole.
When to use them
The concepts and techniques discussed should be applied proactively throughout the development lifecycle. Regular profiling, code reviews focused on memory usage, and thorough testing under load conditions will help identify and address potential OutOfMemoryError
issues before they impact production environments.
Memory footprint
Each Java object has a memory footprint. The object header contains information for the JVM, and the object's fields store data. The size of the object depends on the number and types of its fields. Large collections of objects, especially those containing primitive types or other objects, can quickly consume significant memory. Understanding the size of your objects is critical for estimating memory requirements.
Native Memory Leak
Explanation: If your Java code uses the Java Native Interface (JNI) to interact with native libraries (e.g., C or C++ code), memory leaks in the native code can lead to OutOfMemoryError
. Native memory is not managed by the JVM's garbage collector. In the illustrative (non-runnable without proper setup) example, sun.misc.Unsafe
is used (which requires special permissions) to allocate memory directly. If this memory is not explicitly freed using native code (e.g., free()
in C), it will leak, eventually causing the JVM to crash with an OutOfMemoryError
, but typically a message indicating a native memory exhaustion rather than a heap space issue.
//Example demonstrating native memory allocation (Illustrative only)
//This is NOT a complete, runnable example.
/*
import sun.misc.Unsafe;
public class NativeMemoryLeak {
private static final Unsafe unsafe = getUnsafe(); //Requires reflection or --add-opens
private static final long SIZE = 1024 * 1024; // 1MB
public static void main(String[] args) {
while (true) {
long address = unsafe.allocateMemory(SIZE);
// Memory is allocated but never freed.
// This will eventually cause a crash due to native memory exhaustion.
// System.out.println("Allocated memory at address: " + address);
}
}
private static Unsafe getUnsafe() {
try {
java.lang.reflect.Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe) f.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
*/
Metaspace Exhaustion (PermGen replacement)
Explanation: Metaspace is where the JVM stores class metadata. In older JVMs (pre-Java 8), this was the PermGen space. If the JVM loads a large number of classes, especially dynamically generated classes (e.g., using bytecode manipulation libraries like Javassist or CGLIB), the Metaspace can become exhausted. The above example uses Javassist to continuously generate new classes. Without any mechanism to unload these classes, Metaspace will eventually fill up, leading to an OutOfMemoryError: Metaspace
error. The default size of Metaspace can be configured using -XX:MaxMetaspaceSize
.
import javassist.ClassPool;
import javassist.CtClass;
public class MetaspaceOOM {
public static void main(String[] args) throws Exception {
for (int i = 0; i < 100000; i++) {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass("com.example.GeneratedClass" + i);
ctClass.toClass();
}
}
}
Alternatives
Alternatives to avoid OutOfMemoryError
include:
-Xms
and -Xmx
JVM options to increase the initial and maximum heap sizes, respectively. This provides more memory for object allocation. However, simply increasing heap size is not a long-term solution if there are underlying memory leaks or inefficient memory usage patterns.
Pros
Properly addressing OutOfMemoryError
issues leads to:
Cons
Ignoring OutOfMemoryError
issues can result in:
FAQ
-
How do I determine the optimal heap size for my application?
Determining the optimal heap size requires profiling your application under realistic load conditions. Monitor memory usage, garbage collection frequency, and application performance. Start with a reasonable heap size (e.g., 512MB or 1GB) and gradually increase it until you observe a balance between memory usage and performance. Avoid setting the heap size too large, as this can increase garbage collection pauses.
-
What is the difference between PermGen and Metaspace?
PermGen (Permanent Generation) was used in older JVMs (before Java 8) to store class metadata. Metaspace is the replacement for PermGen in Java 8 and later. The key difference is that Metaspace is allocated from native memory, while PermGen was allocated from the JVM's heap. Metaspace also has a dynamically resizing capability, which reduces the risk of
OutOfMemoryError
compared to PermGen's fixed size. -
How can I diagnose a native memory leak?
Diagnosing native memory leaks requires using native memory analysis tools, such as Valgrind (for Linux) or memory leak detectors specific to the operating system and native libraries used by your application. These tools can identify allocations in native code that are not being properly freed.