All topics
memory management
intermediate

Garbage Collection

Free heap memory automatically by identifying objects no longer reachable from GC roots — and choose the GC algorithm that fits your latency vs throughput target.

Garbage collection is Java freeing memory for you. It finds objects your program can no longer reach, and reclaims the space they were using.

A janitor that throws out anything no longer connected to the main building (GC roots). G1 cleans one room at a time during business hours. ZGC works alongside everyone without disturbing them.

Key Concepts

1
Interview questions are almost never about how it frees memory. They are about the pauses: collecting sometimes stops your application, and you need to know why, for how long, and what to do.
2
How it decides what to free. It does not count references. It starts from a set of definitely-alive places — local variables on each thread's stack, static fields, and a few others — and follows every reference from there, marking everything it can reach.
3
Anything not marked is unreachable, and unreachable means nobody can ever use it again. That is what gets freed.
4
This is why two objects pointing at each other are still collected. Neither is reachable from a starting point, so neither is marked, so both go.
5
Why memory is split into generations. Almost all objects die almost immediately. A method makes a few objects, returns, and they are garbage.
6
So the heap has a young area and an old area. New objects go into the young area, which is collected often. Because nearly everything there is already dead, the collector only has to copy out the few survivors, which is quick.
7
An object that survives several young collections is moved to the old area, which is collected rarely and more expensively.
8
The collectors, and what each is for.
  Serial       stops everything, one thread. Tiny heaps only.
  Parallel     stops everything, many threads. Best total
               throughput; fine for batch jobs.
  G1 (default) splits the heap into regions and collects the
               emptiest first; aims at a pause target you set,
               usually 200 ms.
  ZGC          does nearly all its work while your application
               runs. Pauses under 10 ms even on huge heaps, at a
               few percent throughput.
  Shenandoah   same idea as ZGC.
9
Choose from the requirement, not the name. A nightly batch job can afford a two-second pause and wants maximum throughput. An API where the 99th percentile matters cannot, and should pay a few percent for short pauses.
10
How to tell whether you have a leak. Do not watch memory usage; it rises and falls normally. Watch the level it returns to after a full collection.
11
A healthy application drops back to the same floor each time. A leaking one has a floor that creeps upward over hours. That rising floor is the signal, and it means something is holding references it no longer needs.
12
Sizing the heap. Too small and the collector runs constantly. Too large and each old-area collection takes longer — and if the heap does not fit in physical memory, the machine starts swapping, which is catastrophic because the collector touches memory all over the place.
13
Set the starting size equal to the maximum size in production, so the JVM never pauses to resize.
14
System.gc() is a suggestion, and usually a bad one. It asks for the most expensive kind of collection available, and the JVM may ignore it anyway. Code that calls it in a loop turns a mild memory problem into a pause problem.
System.gc()
15
What the interviewer is probing.1. "How does it know what to free?" Probing: reachability. Stalls: "Reference counting." *Moves up:* mark from live starting points; unreachable is garbage, which is why cycles are collected.
16
2. "Why split young and old?" Probing: the observation behind it. Stalls: "For organisation." Moves up: most objects die young, so collecting a small young area frequently reclaims most garbage cheaply.
17
3. "Which collector would you choose?" Probing: requirement first. Stalls: naming one. Moves up: pause target versus throughput, with the workload stated; G1 as a sensible default.
18
4. "How do you find a leak?" Probing: method. Stalls: "Increase the heap." Moves up: watch the post-collection floor over hours, then take two heap snapshots and compare what grew.
19
5. "Does System.gc() help?" Probing: judgement. Stalls: "It frees memory." Moves up: it requests the most expensive collection and may be ignored; it usually makes pauses worse.
System.gc()
20
  Collectors, and what each is for
21
  Collector    Pause behaviour        Throughput   Use when
  ------------------------------------------------------------------
  Serial       stop-the-world, grows  good         tiny heaps, single
               with heap                           core, containers
  Parallel     stop-the-world, multi- best         batch jobs; pauses
               threaded                            are acceptable
  G1 (default) target-driven, often   very good    general purpose,
               50-200 ms                           heaps 4 GB+
  ZGC          < 10 ms, near heap-    ~5% less     latency-critical,
               independent                         large heaps
  Shenandoah   < 10 ms, concurrent    ~5% less     same, OpenJDK
               compaction
22
  Object lifecycle
23
    allocate -> Eden
      minor GC: live objects copied Eden -> Survivor
      survive N collections (MaxTenuringThreshold, default 15)
        -> promoted to Old
      Old fills -> major/mixed collection (the expensive one)
24
  Reading GC logs (-Xlog:gc*)
    frequent minor GCs          -> high allocation rate; look for
                                   short-lived garbage in hot loops
    growing old-gen after each
      full GC                   -> a real leak; take a heap dump
    long pauses, low throughput -> heap too small, or the live set is
                                   genuinely large
    "to-space exhausted" (G1)   -> promotion failing; raise heap or
                                   lower the pause target's ambition
25
  Flags worth knowing
    -XX:+UseG1GC  -XX:MaxGCPauseMillis=200
    -XX:+UseZGC
    -Xms == -Xmx                 avoids resize pauses
    -Xlog:gc*:file=gc.log        always on in production