Why garbage collection is a core interview topic
Garbage collection is where "Java manages memory for you" meets "and here is why your service still ran out of memory." Interviewers ask about it because understanding GC separates developers who can diagnose a slow, pausing, or leaking service from those who can only restart it. The questions build on one idea — reachability — and reward candidates who can explain why an object was or was not collected.
This page covers the GC questions asked most, from reachability to modern collectors to leaks. It pairs directly with the memory management questions and the JVM set, which cover the heap regions GC operates on.
Q1. How does the garbage collector decide what to collect?
By reachability. Starting from GC roots — active thread stack frames, static fields, JNI references and a few others — the collector traces every object reachable through references. Any object it cannot reach is garbage, regardless of how many references point between dead objects.
This is why Java does not use naive reference counting: two objects that reference each other but are unreachable from any root are still collected, whereas reference counting would keep them alive forever. Stating "unreachable, not unreferenced" is the crisp distinction interviewers listen for, because it disproves the common myth that circular references leak in Java.
Q2. Explain the generational heap.
The heap is split into a young generation — Eden plus two survivor spaces — and an old generation, based on the weak generational hypothesis: most objects die young. New objects allocate in Eden; those surviving a collection move to a survivor space, and after surviving several collections they are promoted to the old generation.
Young Gen: [ Eden ][ Survivor S0 ][ Survivor S1 ] -> minor GC, frequent
Old Gen: [ long-lived, promoted objects ] -> major GC, infrequent
The design pays off because collecting the small, mostly-dead young generation is cheap and can be done often, while the stable old generation is collected rarely. Understanding promotion — objects earning their way to old after surviving young collections — is what makes the next question (minor vs major) fall out naturally.
Q3. What is the difference between minor, major and full GC?
A minor GC collects only the young generation: fast, frequent, and it pauses briefly. A major GC collects the old generation; a full GC collects the entire heap (young + old) and often metaspace, causing the longest pauses. Frequent full GCs are a red flag — usually a leak or an undersized heap, not a tuning need.
The operational insight interviewers want: you diagnose GC health by watching pause frequency and duration, and a service that full-GCs constantly is fighting to reclaim memory that keeps filling back up — which points to a leak upstream, not a GC parameter. That reasoning is more valuable than memorizing collector names.
Q4. What are the modern collectors and when would you pick one?
G1 (the default since Java 9) divides the heap into regions and targets a configurable pause-time goal, balancing throughput and latency for most applications. ZGC and Shenandoah are low-latency collectors that keep pauses in the low-millisecond range even on very large heaps, at some throughput cost. The old Parallel collector maximizes throughput when pause time does not matter (batch jobs).
You are not expected to memorize flags. The judgement to show is matching a collector to a goal: latency-sensitive service with a big heap → ZGC; balanced default → G1; throughput-only batch → Parallel. Adding "and I would confirm with real pause measurements, not assumptions" is the senior close.
Q5. What are GC roots?
GC roots are the starting points from which reachability is traced: local variables and operands in active thread stacks, static fields of loaded classes, JNI (native) references, and live threads themselves. An object is alive if and only if a path of references leads to it from at least one root.
This ties back to leaks: a static field is a GC root, so anything a growing static collection holds is permanently reachable and never collected. Being able to name a couple of root types — thread stacks and static fields especially — lets you explain both why objects survive and how leaks form.
Q6. Why do memory leaks happen in a garbage-collected language?
Because a leak in Java is an object that is reachable but unneeded. The GC only frees unreachable objects, so if a live reference lingers, the object stays. Classic causes: an ever-growing static collection, an unbounded cache with no eviction, listeners/callbacks never unregistered, and ThreadLocal values left on pooled threads.
// Leak: a static collection is a GC root, so entries are never collected
class Registry {
static final List<Session> ACTIVE = new ArrayList<>();
void open(Session s) { ACTIVE.add(s); }
// no matching remove(): ACTIVE grows forever, sessions never freed
}
The fix is always "remove the reference when done" — a matching remove, a bounded cache with eviction, WeakReference/WeakHashMap where appropriate, and threadLocal.remove() in a finally. Naming a specific cause and its fix is what turns this from theory into experience.
Q7. What is the role of finalize(), and why avoid it?
finalize() was a method the GC could call before reclaiming an object, intended for cleanup. It is deprecated and should not be used: it runs unpredictably (or never), can resurrect objects, delays collection, and hurts performance. For resource cleanup, use try-with-resources (AutoCloseable) or Cleaner.
// Deterministic cleanup — not finalize()
try (var input = Files.newInputStream(path)) {
// use input; close() is guaranteed on exit, even on exception
}
The point interviewers verify: never rely on the GC for releasing files, sockets or connections, because you cannot predict when — or whether — collection runs. Deterministic release via try-with-resources is the correct pattern, and knowing finalize is deprecated shows you are current.
Q8. What are strong, soft, weak and phantom references?
A strong reference (the normal kind) prevents collection. A soft reference is collected only when memory is low — useful for memory-sensitive caches. A weak reference is collected at the next GC once no strong reference remains — the basis of WeakHashMap. A phantom reference is used with a ReferenceQueue for post-collection cleanup, replacing finalize.
The practical mapping is the answer: soft for caches that should survive until memory pressure, weak for canonicalizing maps and listener registries that must not prevent collection, phantom for advanced cleanup scheduling. Knowing that WeakHashMap keys disappear when otherwise unreferenced connects the concept to a class you can actually use.
How to prepare
Anchor the whole topic on reachability from GC roots — once that clicks, minor/major GC, why circular references are fine, and why leaks still happen all follow from it. Write the leak demo (a static list that only grows), watch heap usage climb, then fix it, because seeing a leak beats reading about one. Connect this to the memory management questions for the heap regions and the JVM set for how the collector fits the runtime. Then rehearse a "how would you diagnose a leaking service?" answer — reproduce, heap dump, find the dominant retained graph — in a mock interview, since that scenario is the one senior GC questions build toward.
Frequently Asked Questions
How does Java decide an object can be garbage collected?
What is generational garbage collection?
What is the difference between minor and major GC?
Can you force garbage collection in Java?
If Java has garbage collection, why do memory leaks still happen?
Want to Build Your Career in Java Full Stack with AI?
Join CodeBegun and train with working industry engineers — Explore the Java Full Stack program

