Java OutOfMemoryError Explained – Types, Real Examples, Fixes, and Debugging Guide

Java OutOfMemoryError Explained – Types, Causes, Fixes, and Real Scenarios Learn the different types of OutOfMemoryError (OOME) in Java with real-world scenarios, code examples, debugging tips, and solutions. Covers heap space, GC overhead, Metaspace, Direct buffer memory, native threads, and array size issues.

🔥 Java OutOfMemoryError – Complete Guide

When a Java application runs out of memory, the JVM throws an OutOfMemoryError. But not all OOMEs are the same. Each type points to a specific issue in your system. In this blog, we’ll explore different OOME types, real-world scenarios, code samples, and fixes.

1️⃣ Java Heap Space

Cause: Heap (object memory) is exhausted and garbage collection cannot free enough space.


// Example: Memory Leak with List
List list = new ArrayList<>();
while (true) {
    list.add(new byte[1024 * 1024]); // keeps filling heap
}
      

Real-World Scenario: A Spring Boot app keeps caching user sessions in memory but never evicts old ones → heap grows until OOME.

Fix: Increase -Xmx, configure cache eviction, or use Redis/DB-backed session storage.

2️⃣ GC Overhead Limit Exceeded

Cause: JVM spends >98% of timein GC but recovers <2% of memory.

Real-World Scenario: A batch job loads millions of rows into memory at once.


// Bad: Loading everything into memory
List<User> users = userRepository.findAll(); 
// Fix: Use streaming / pagination
Page<User> users = userRepository.findAll(PageRequest.of(0,1000));
      

Fix: Stream results, paginate queries, increase heap only if necessary.

3️⃣ Metaspace (Java 8+) / PermGen (Java 7-)

Cause: Too many classes loaded, often due to classloader leaks.

Real-World Scenario: An app redeploys repeatedly in Tomcat without releasing old classloaders → Metaspace grows.


// Example: Dynamic class generation (can cause leaks)
ClassPool pool = ClassPool.getDefault();
for (int i = 0; i < 100000; i++) {
    CtClass cc = pool.makeClass("Class" + i);
    cc.toClass();
}
      

Fix: Increase -XX:MaxMetaspaceSize, unload unused classes, fix classloader leaks.

4️⃣ Direct Buffer Memory

Cause: Off-heap memory exhausted via ByteBuffer.allocateDirect().


while (true) {
    ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024);
}
      

Real-World Scenario: High-performance I/O service keeps allocating direct buffers without releasing them.

Fix: Release buffers properly, use pooled buffers (e.g., Netty), increase -XX:MaxDirectMemorySize.

5️⃣ Unable to Create New Native Thread

Cause: JVM cannot create new OS threads due to memory or system limits.


// Spawning too many threads
for (int i = 0; i < 100000; i++) {
    new Thread(() -> {
        try { Thread.sleep(Long.MAX_VALUE); } catch (Exception e)

Post a Comment

0 Comments

Close Menu