Java in Docker and Kubernetes: avoid OOMKilled

The pod restarts. kubectl get pod shows OOMKilled, and the exit code is 137. But the logs show no OutOfMemoryError, and the heap was far from full. The JVM did not run out of memory: the container did. The Linux kernel killed the process because it went over its limit. This post shows what the JVM sees in a container, what adds to the heap, how to reproduce the kill, and how to set the memory so that everything fits. All the examples are in the containers/ folder of the public java-gc-lab repository. They use the official eclipse-temurin images: no image to build. ...

October 23, 2026 · 10 min

Java heap dump: find a memory leak with Eclipse MAT

In the post about memory leaks, two jcmd histograms showed which class grows. But not what keeps it alive. For that, you need a heap dump: a picture of the whole heap, with the objects and the links between them. We take the same leak again: UnboundedCache.java, a static HashMap cache that gets a new key on each request. We analyze it with Eclipse Memory Analyzer (MAT), an open source tool that Oracle’s guide names for heap dump analysis. All the commands are in the heap-dump folder of the java-gc-lab repository. ...

October 23, 2026 · 4 min

Java memory leaks: 6 classic cases, reproduced

In Java, the garbage collector frees the objects that nothing references anymore. So a memory leak is an object that is no longer useful but still referenced: the GC cannot free it. Oracle’s guide puts it this way: the application keeps references to objects “unintentionally”. Here are six classic leaks, each one reproduced by a small program of the public repository java-gc-lab, with what you see and the fix. What keeps an object alive The chain of references of a leak: a GC root, the static field AUDIT of the class StaticCollection, refers to an ArrayList, which refers to Order objects; they stay alive. A short-lived object that nothing refers to anymore is freed by the GC. What keeps an object alive every object reachable from a root stays alive GC root static AUDIT ArrayList the list of the field Order Order Order Order objects useless, but reachable stay alive: the leak short-lived object no reference left → freed by the GC The GC starts from “roots”: the static fields of the loaded classes, the local variables of the running threads… Every object that can be reached from a root stays alive. A leak is a path that someone forgot to cut. ...

October 23, 2026 · 6 min

Java OutOfMemoryError: every message, reproduced

An OutOfMemoryError is not one single problem. The text after the colon tells you which memory area is full: the heap, the Metaspace, the direct memory or the threads. And each area is checked and fixed in a different way. For each message, I wrote a small program that causes it. They are in the public repository java-gc-lab, one file per message, to run with java without compiling first (Java 11 or later). ...

October 22, 2026 · 8 min

GC generations and cycles: young, old, minor, major, full

Minor GC, major GC, full GC: you see these words everywhere, but not always with the meaning the official documentation gives them. Most objects die young. So the JVM keeps new objects in the young generation and long-lived ones in the old generation. A GC that only cleans the young generation is fast, and it runs often. Why generations? Oracle’s official guide to the garbage collector starts from a simple fact: most objects live for a very short time. This is called the generational hypothesis. An object created for a request, a loop or a calculation is useless almost right away. ...

October 22, 2026 · 6 min

JVM memory: heap, stack and Metaspace

Does your old Java configuration fail to start on Java 17? The answer is in how the JVM uses memory. In Java, each thread has its own stack for local variables. Objects live on the heap, the area the garbage collector cleans up. Class data lives in the Metaspace, outside the heap. The JVM also uses other memory areas that are easy to forget. Three areas you need to know Memory of a Java process: thread stacks, the heap, the Metaspace and other native memory areas Java process everything outside the heap is native memory Stacks one per thread · -Xss Thread main process() · client main() Thread 2 read() run() frames, local variables Heap shared · managed by the GC · -Xmx Client String List byte[] objects created with new interned strings static fields Metaspace native memory no limit by default Client fields, methods… String fields, methods… class descriptions -XX:MaxMetaspaceSize Also in native memory Code cache machine code from the JIT up to 240 MB by default Direct memory ByteBuffer.allocateDirect() -XX:MaxDirectMemorySize Internal data garbage collector, compiler and more The stack: one per thread Each thread gets its own stack. Every method call adds a frame to it, with the method’s parameters and local variables. When the method returns, its frame is gone. The garbage collector is not needed to free this memory. ...

October 22, 2026 · 5 min