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.
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.
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.
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.
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
.
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)
0 Comments