Skip to content
Lesson 4 of 7

Step 1 of 7 · Reading · ~3 min

Learn

Reflection and JVM

JVM Memory and GC Basics

You will be able to say which region of the JVM a piece of data lives in, which objects the garbage collector may free and why, and which error you get when each region runs out. The exercise has you do the first step of a collector by hand: find what is still reachable.

The regions

  • Heap: every object made with new. It is split into a young generation (new objects, collected often and cheaply) and an old generation (objects that survived several young collections).
  • Thread stacks: one per thread, holding a frame for each active method call: local primitives and references. Its size is -Xss.
  • Metaspace: class metadata (it replaced PermGen in Java 8).
  • Native memory: direct ByteBuffers, JIT code, thread structures. The heap flags do not limit it.

Two limits fail differently. Too many live objects gives OutOfMemoryError: Java heap space (cap set by -Xmx). Too many nested calls gives StackOverflowError (cap set by -Xss). Raising -Xmx does nothing for a stack overflow.

What the collector may free

Java does not count references. An object is live if some chain of references leads to it from a GC root; everything else is garbage, even if garbage objects point at each other. The roots are:

  • local variables and parameters in the frames of running threads
  • static fields of loaded classes
  • live threads themselves, and references held by native code

The collector marks everything reachable from the roots and reclaims the rest. A worked case on a different graph: objects P, Q, R, S, T, where P -> Q, Q -> R, S -> T, T -> S, and the only root is a local variable holding P. Marking reaches P, Q, R. S and T form a cycle, but nothing live reaches them, so both are garbage. A reference-counting scheme would leak them (each has count 1); a tracing collector does not. Direction matters: if S also pointed at P, that would not make S live.

Generations and collectors

Most objects die young. New objects go to eden; when it fills, a minor GC copies the survivors to a survivor space, and objects that survive enough rounds are promoted to the old generation. A full GC collects everything and pauses longest. G1 has been the default since Java 9; it splits the heap into regions and aims for a pause target (-XX:MaxGCPauseMillis). ZGC and Shenandoah target very short pauses on big heaps. System.gc() is only a request.

Reference strength

A normal reference is strong. A SoftReference is cleared only under memory pressure (caches). A WeakReference is cleared at the next collection once nothing strong reaches the object (WeakHashMap, canonicalising maps). finalize() is deprecated; use try-with-resources or Cleaner.

Leaks in a garbage-collected language

A leak is an object that is reachable but never used again: a static List that only grows, a listener never removed, a ThreadLocal in a pooled thread. Take a heap dump (jcmd <pid> GC.heap_dump file.hprof) and follow the path from a GC root to the leaked object.

Walking a long chain without the stack

A recursive walk uses one stack frame per link, so a chain of 100,000 nodes ends in StackOverflowError even though the heap is nearly empty. The cure is to keep the pending nodes on the heap yourself:

Deque<Node> pending = new ArrayDeque<>();
pending.push(first);
while (!pending.isEmpty()) {
    Node n = pending.pop();
    total += n.weight;
    if (n.next != null) pending.push(n.next);
}

Common mistakes

  • Marking recursively. It passes small tests and dies with StackOverflowError on a long chain.
  • Not recording visited objects. A cycle makes the walk run forever.
  • Treating a reference out of a garbage object as a root. Only the roots start the walk.
  • Reading -Xmx as a stack limit. The heap and stack limits are separate.

Your exercise

Implement the mark phase over a small object graph and report what a collector would keep and free. The graph is built for you; you return which objects are reachable.

Up nextGenerics In DepthGenerics, Streams, and Functional Java

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…