Garbage Collection

Learning goals

By the end of this chapter you should be able to:

  • Define garbage in terms of reachability from GC roots
  • Explain young vs old generations and why minor GC is frequent
  • Compare collector families (G1, ZGC, Parallel) at a high level
  • Connect application patterns (caches, listeners, thread locals) to memory leaks

Why GC follows memory structure

In the previous chapter you placed objects on the heap and saw generations (Eden, survivors, old). Garbage collection is how the JVM reclaims unreachable objects in that heap — without you calling free.

String temp = new String("data");
temp = null;  // prior String may become eligible for collection

Eligible ≠ collected immediately. The GC runs when allocation pressure or explicit hints (System.gc() — usually ignore) trigger a cycle.

The sections below walk through what gets collected, when pauses happen, and which collector fits which workload — building on your heap layout notes.



Overview

In C/C++ you call free() yourself. In Java, the garbage collector (GC) does it for you — it scans the heap, finds objects nothing is pointing to anymore, and reclaims their memory.

The trade-off: the JVM occasionally pauses your app to do this. Interview questions are mostly about how the GC decides what to collect, when it pauses, and how to pick the right collector for your workload.


Table of contents

  1. What “garbage” means
  2. Heap layout: young vs old generation
  3. Minor GC vs Major GC vs Full GC
  4. Stop-the-world pauses
  5. The GC algorithms
  6. Tuning basics
  7. Common interview questions
  8. Summary

1. What “garbage” means

An object is garbage when no live reference reaches it from a GC root (local variables, static fields, thread stacks, JNI refs).

String a = new String("hello");   // object reachable from local 'a'
a = null;                          // now the "hello" object is unreachable → eligible for GC

The GC’s job: find these unreachable objects and reclaim the memory.


2. Heap layout: young vs old generation

The JVM heap is split into generations because of one observation — most objects die young. So the JVM collects new objects often (cheap) and old ones rarely (expensive).

+---------- Heap ----------+
|  Young Generation        |
|   ├─ Eden                |   <- new objects land here
|   ├─ Survivor S0         |
|   └─ Survivor S1         |
+--------------------------+
|  Old (Tenured) Generation|   <- long-lived objects
+--------------------------+

How an object moves:

  1. new puts it in Eden.
  2. When Eden fills → Minor GC runs. Surviving objects move to a Survivor space.
  3. After surviving a few Minor GCs (the tenuring threshold), the object gets promoted to Old.

A separate area called Metaspace holds class metadata (replaced “PermGen” in Java 8+). It lives outside the heap.


3. Minor GC vs Major GC vs Full GC

Type What it collects Speed Pause
Minor GC Young generation only Fast (ms) Short stop-the-world
Major GC Old generation Slow (often hundreds of ms) Longer pause
Full GC Young + Old + Metaspace Slowest Worst-case pause

You want most collections to be Minor. Frequent Full GCs are a red flag — usually means the old generation is too small or you’re leaking memory.


4. Stop-the-world pauses

Most GCs freeze every application thread for part of their work. That’s a stop-the-world (STW) pause.

  • During STW, no requests are processed.
  • Latency-sensitive apps (trading, real-time) care a lot about pause length.
  • Modern collectors (G1, ZGC, Shenandoah) shrink STW down to milliseconds.

5. The GC algorithms

The four collectors that come up in interviews:

Serial GC

  • One thread does all the work, app threads freeze.
  • For single-core / tiny heap apps (think CLI tools, small containers).
  • Flag: -XX:+UseSerialGC.

Parallel GC (a.k.a. Throughput collector)

  • Multiple threads do the GC work in parallel — still stop-the-world, but faster.
  • Optimised for throughput, not latency.
  • Was the default until Java 8.
  • Flag: -XX:+UseParallelGC.

G1 GC (Garbage First)

  • Divides the heap into many small regions instead of fixed young/old areas.
  • Tries to hit a pause-time target (e.g. 200 ms) by collecting only the regions with the most garbage first.
  • Default since Java 9. Good general-purpose choice.
  • Flag: -XX:+UseG1GC (already on by default in modern JDKs).

ZGC

  • Low-latency collector designed for sub-millisecond pauses, even with multi-terabyte heaps.
  • Does most work concurrently with the app.
  • Use for latency-sensitive services on large heaps.
  • Flag: -XX:+UseZGC. Production-ready from Java 15.
Collector Best for Pause
Serial Tiny apps, single core Long
Parallel Throughput-heavy batch jobs Medium
G1 (default) Most server apps ~200 ms target
ZGC Latency-critical, big heaps <1 ms

Shenandoah (Red Hat) is similar to ZGC and also worth knowing by name.

Older “CMS” (Concurrent Mark Sweep) is deprecated since Java 9 and removed in Java 14. Mention it only if asked about legacy systems.


6. Tuning basics

You don’t need to know every flag. The ones interviewers actually ask about:

Flag Meaning
-Xms512m Initial heap size
-Xmx2g Maximum heap size
-XX:MaxRAMPercentage=75 Use 75% of the container’s memory (better than -Xmx in Docker/K8s)
-XX:+UseG1GC Pick G1
-XX:MaxGCPauseMillis=200 G1 pause-time target
-Xlog:gc*:file=gc.log GC logging (replaces older -XX:+PrintGCDetails)
-XX:+HeapDumpOnOutOfMemoryError Dump heap when OOM happens — helps debug leaks

Rule of thumb: start with defaults (G1) and MaxRAMPercentage. Tune only after measuring with GC logs.


7. Common interview questions

Q: Can you force the garbage collector to run? A: You can suggest it with System.gc(), but the JVM is free to ignore it. Don’t rely on it in production code.

Q: What is the difference between Minor GC and Full GC? A: Minor GC collects only the young generation — fast, frequent, short pause. Full GC collects young + old + metaspace — slow and rare. Frequent Full GCs usually mean a memory leak or wrongly sized old generation.

Q: What is stop-the-world? A: A pause where the GC freezes all application threads to do its work. Modern collectors like G1 and ZGC shrink this to a few milliseconds.

Q: Which GC is the default in modern Java? A: G1 since Java 9. ZGC for low-latency apps with large heaps.

Q: What is a memory leak in Java? A: Objects you don’t need any more are still reachable from a GC root, so the GC can’t collect them. Common causes: static collections that keep growing, listeners not unregistered, ThreadLocal not cleaned up.

Q: What replaced PermGen in Java 8? A: Metaspace, which lives in native memory (not the heap). PermGen had a fixed size and caused OutOfMemoryError: PermGen space; Metaspace grows as needed (you can cap it with -XX:MaxMetaspaceSize).

Q: What’s the difference between heap and stack memory? A: Stack holds method frames and local variables; one stack per thread; small (~256 KB–1 MB), freed when methods return. Heap holds all objects (new allocations); shared across threads; managed by the GC.

Q: What is a strong / soft / weak / phantom reference? A:

  • Strong — normal reference. Object lives as long as a strong ref exists.
  • Soft — collected only if the JVM needs memory. Used for caches.
  • Weak — collected at the next GC. Used in WeakHashMap.
  • Phantom — never returns the object; used to track when GC has finalised it.

Q: How would you diagnose a memory leak in production? A: Enable GC logs, take a heap dump (jmap -dump or -XX:+HeapDumpOnOutOfMemoryError), open it in Eclipse MAT or VisualVM, look at the dominator tree for big retained objects.


8. Summary

Concept One line
Garbage Unreachable objects on the heap
Generations Young (Eden + Survivors) + Old + Metaspace
Minor GC Cleans young — fast, frequent
Full GC Cleans everything — slow, rare
STW App pauses while GC runs
Default G1 since Java 9
Low latency ZGC for sub-millisecond pauses
Tuning Start with MaxRAMPercentage + GC logs

Next steps


Happy Learning


Practice checkpoint

  1. Object A references B, B references A, nothing else references either — collected? Answer: yes — cycle unreachable from GC roots is garbage.

  2. Minor GC vs Full GC — which is usually cheaper? Answer: Minor GC (young generation only).

  3. Long-lived cache grows old gen until Full GC thrashing — likely fix direction? Answer: cap/evict cache, increase heap if legitimate, or fix leak; tune collector second.

  4. What is a GC root? Answer: starting points: thread stack locals, static fields, JNI refs, etc.


Interview angles

  • Reachability vs reference counting: JVM uses tracing; reference counting alone fails on cycles.
  • Stop-the-world: most collectors pause app threads briefly; ZGC/Shenandoah target low ms pauses.
  • Generational hypothesis: most objects die young → Eden collection is hot path.
  • Leak definition: reachable but unused — static map holding everything is classic leak pattern.

Next: /java/learn/jvm/java-versions/