dagblog.
← Archive

Memory leaks in Java


A memory leak happens when a program consumes memory and doesn’t let it go even when it no longer needs it. In Java,
the garbage collector (GC), known in Czech as Sběrač Neplatných Objektů, frees memory.
The Java Virtual Machine handles it entirely. The programmer doesn’t have to free
memory manually as in C, for example.

Unfortunately, Java programs can still leak memory and end up with an Out of Memory message. To explain why, we need a simplified picture of how the GC works and of Java’s memory model. The stack and the heap are its foundation. Any object created in Java lives on the heap.

new Object(); //na haldě vznikne nový objekt

When we create a variable in Java, a slot is created on the stack.

Object o; //na zásobníku vznikne prázdný záznam, na který odkazuje proměnná o

Put it all together in one piece of code, Object o = new Object();, and
we have the variable o referring to a slot on the stack. Calling
the constructor creates an object on the heap. The assignment puts
a reference to the heap object into the stack slot.

A heap object referenced from the stack is considered
live. If nothing on the stack points to it, it’s considered dead and the GC
can remove it from the heap, freeing memory. To illustrate, take our example
and put it in an ordinary method.

class Foo{
void doSomething(){
Object o = new Object();
}
}

Calling doSomething creates a stack slot holding a reference
to the object allocated on the heap. When the method finishes, the local variable o goes away, along with its slot. Now the object on the heap has no reference from the stack.

When the JVM decides it needs to free memory, it runs the GC. The GC walks the stack and marks every heap object referenced from there as live. It can then remove the other objects and free their memory together.

Consider a class responsible for returning an employee’s personal
identification number.

public class RodnaCisla{
private static Map cisla = new HashMap();

public String getRC(Integer id){
String rc = cisla.get(id);
if(rc == null) {
rc = ...//získej číslo např. z DB
cisla.put(id, rc);
}
return rc;
}
}

All this class does is put employee numbers it has already fetched
into a static map. Whenever someone requests a number
it has found before, the class can return it from
the map without hitting the database.

In terms of the memory model, we have a variable cisla on the stack. It’s static, shared by all instances of RodnaCisla, and created when the class loader loads RodnaCisla. Every time someone requests a number we haven’t found yet, that number is also stored in the map referenced by cisla.

Over time, the heap accumulates objects referenced from the static map. The GC can’t clear them away because they’re still live. As more and more identification numbers accumulate, they occupy memory that won’t be freed. That’s a memory leak.

This simple example shows how a poor caching implementation can
cause a memory leak that then takes painstaking work to find through profiling. A profiler is the tool used to detect it. In profiling mode, the JVM provides information through a standardized interface that lets the profiler detect the leak.

That long detour brings us to Staffan Larsen’s article Memory Leaks, Be Gone.

Medium version

Related articles

Related CZ Podcast episodes