Skip to content
  • Garbage Collection · Java

Garbage Collection

LearnViaNotes @learnvianotes· 4 min read

The garbage collector frees heap objects that are no longer reachable — you never call free, but you still decide what stays reachable.

GC roots and reachability

  • Local variables and parameters in every live thread's stack frames.

  • Static fields of loaded classes.

  • Active threads themselves.

  • JNI references held by native code, and objects used as monitors (synchronized).

Java
public class Main {
    static class Node { Node next; }

    public static void main(String[] args) {
        Node a = new Node();
        Node b = new Node();
        a.next = b;
        b.next = a;          // a cycle: a → b → a

        a = null;
        b = null;            // no root reaches the cycle any more → both are garbage
        System.out.println("the cycle is unreachable and will be collected");
    }
}
Output
the cycle is unreachable and will be collected

Generational collection

Weak generational hypothesis: most objects die young (temporary strings, iterators, DTOs). So the heap is split and the young part is collected often and cheaply.

  1. Allocate in Eden

    new bumps a pointer in a thread-local allocation buffer — allocation is extremely cheap.

  2. Minor GC

    When Eden fills, live objects are copied to a Survivor space; everything left in Eden is reclaimed at once.

  3. Age and promote

    Each survived minor GC increments an object's age; past a threshold it is promoted to the old generation.

  4. Major / full GC

    When the old generation fills, it is collected too — more expensive, historically the source of long pauses.

Phase / termMeaning
MarkTrace from the roots and mark every reachable object.
SweepReclaim the memory of unmarked objects.
CompactSlide live objects together to remove fragmentation.
CopyingCopy live objects to an empty space (used for the young gen: cost ∝ live objects, not heap size).
Stop-the-world (STW)All application threads are paused while the GC works.
ConcurrentGC work done while application threads keep running.

The collectors

CollectorFlagBest forNotes
Serial-XX:+UseSerialGCTiny heaps, single-core containersOne thread, stop-the-world
Parallel-XX:+UseParallelGCBatch jobs — maximum throughputMany threads, still STW
G1 (default since 9)-XX:+UseG1GCGeneral purpose, heaps of GBsRegion-based; targets a pause goal (-XX:MaxGCPauseMillis=200)
ZGC-XX:+UseZGC (generational in 21: -XX:+ZGenerational)Huge heaps, low latencyPauses under a millisecond, mostly concurrent
Shenandoah-XX:+UseShenandoahGCLow latencyConcurrent compaction (OpenJDK builds)
CMS——Deprecated in 9, removed in 14
Terminal
$ java -XX:+UseZGC -Xmx4g -Xlog:gc App.java      # choose a collector, log each GC
$ java -Xlog:gc*:file=gc.log App.java            # detailed GC log to a file
$ jcmd <pid> GC.heap_info                        # inspect a running JVM

System.gc(), finalize() and cleanup

Java
import java.lang.ref.Cleaner;

public class Main {
    static final Cleaner CLEANER = Cleaner.create();

    static class Resource implements AutoCloseable {
        private final Cleaner.Cleanable cleanable;

        Resource(String name) {
            // the cleanup action must NOT capture `this`, or the object never becomes unreachable
            cleanable = CLEANER.register(this, () -> System.out.println("released " + name));
        }

        @Override
        public void close() { cleanable.clean(); }   // runs the action at most once
    }

    public static void main(String[] args) {
        try (Resource r = new Resource("db-connection")) {
            System.out.println("using the resource");
        }
        System.out.println("after try");
    }
}
Output
using the resource
released db-connection
after try

Tuning in practice

  1. Measure first: GC logs (-Xlog:gc), JDK Flight Recorder (-XX:StartFlightRecording), jstat -gc <pid>.

  2. Size the heap: set -Xms = -Xmx for servers to avoid resizing.

  3. Pick the collector by goal: throughput → Parallel, balanced → G1, latency → ZGC.

  4. Reduce garbage: reuse builders, avoid needless boxing, stream large files instead of loading them.

Can an object be garbage collected while it still references other objects?

What is a stop-the-world pause and how do modern collectors reduce it?

Minor GC vs major GC?

Does calling System.gc() guarantee collection?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report