How does garbage collection work on the JVM, and how would you decide which collector to use for a service?
technical-conceptual · Mid level · software-engineering
What the interviewer is really asking
Assesses whether the candidate understands generational GC mechanics and can map collector choice to a workload's latency-versus-throughput needs, rather than naming collectors with no reasoning.
What to say
- Explain the generational hypothesis: most objects die young, so the heap is split into young and old generations. Cheap, frequent minor collections sweep the young generation; expensive major/full collections handle the old generation.
- Contrast the modern collectors by goal: G1 (the default since Java 9) balances throughput and pause times for general server workloads; ZGC — generational since JDK 21 and the default ZGC mode from JDK 23 — targets sub-millisecond pauses on large heaps; Parallel GC maximizes raw throughput for batch jobs that tolerate pauses.
- Decide from data: pick based on whether the service is latency-sensitive or throughput-sensitive, then verify with GC logs and tail-latency (p99) measurements rather than guessing — and tune heap sizing before swapping collectors.
What to avoid
- Claim you have to free memory manually or call System.gc() to manage it — that defeats the point and a forced full GC usually hurts.
- Say one collector is universally best; the right answer depends on heap size and whether you optimize for pause time or throughput.
- Recommend a collector with no mention of measuring pauses, allocation rate, or heap size first.
Example answers
Strong: On a low-latency trading-adjacent API with a 32GB heap, G1's pauses were spiking our p99. I moved to Generational ZGC, and GC pauses dropped from tens of milliseconds to under a millisecond, which pulled our tail latency back inside SLA. For a nightly batch job on the same platform I left it on Parallel GC because throughput mattered and pauses didn't.
Weak: The JVM has a garbage collector that cleans up unused objects automatically, so memory management isn't really something you worry about in Java. I'd just use whatever the default is.