Garbage Collection
AdvancedJava's garbage collector automatically reclaims unused heap memory. Understanding GC algorithms and tuning flags prevents GC pauses and memory leaks.
Overview
Java's garbage collector traces live objects from GC roots and reclaims unreachable ones. The heap is divided into generations: Young (Eden + Survivors) for short-lived objects, Old (Tenured) for long-lived objects. Modern collectors: G1GC (Java 9+ default), ZGC (Java 15+ production, sub-millisecond pauses), Shenandoah. Understanding GC logs, pause times, and throughput trade-offs is essential for production performance tuning.
Generational GC and Collection Types
Most objects die young (generational hypothesis). Eden is where new objects are allocated. Surviving objects are copied to Survivor spaces; after several GCs they are promoted to Old Gen.
Minor GC: collects Young Gen — fast, frequent. Major/Full GC: collects Old Gen (and sometimes Young) — slow, should be rare. Stop-The-World (STW) pauses all application threads during collection.
/*
Object lifecycle in generational GC:
new Object()
│
▼
┌─────────┐ Minor GC ┌──────┐ N GCs ┌──────────┐
│ Eden │ ─────────► │ S0/S1│ ───────► │ Old Gen │
└─────────┘ └──────┘ └──────────┘
Most objects Survivors Long-lived
die here bounce S0↔S1 objects
(very fast GC) (age threshold) (expensive GC)
*/
// GC tuning flags
// -Xms512m — initial heap size
// -Xmx2g — maximum heap size
// -Xmn512m — Young Gen size
// -XX:+UseG1GC — use G1 collector (default Java 9+)
// -XX:+UseZGC — use ZGC (Java 15+, sub-ms pauses)
// -XX:MaxGCPauseMillis=200 — G1 pause time target
// -XX:+PrintGCDetails — verbose GC logging
// -Xlog:gc*:file=gc.log — GC log to file (Java 9+)G1GC — Garbage First
G1GC (default since Java 9) divides the heap into equal-sized regions (~1–32 MB each) rather than fixed Young/Old areas. It prioritises regions with the most garbage (hence "Garbage First").
G1 aims to meet a configurable pause time target (-XX:MaxGCPauseMillis). It runs concurrent marking in the background to avoid long STW pauses. Suitable for heaps 4 GB – 32 GB with pause requirements.
// G1GC key flags
// -XX:+UseG1GC (default Java 9+)
// -XX:MaxGCPauseMillis=200 target max pause (default 200ms)
// -XX:G1HeapRegionSize=16m region size (1m–32m, power of 2)
// -XX:InitiatingHeapOccupancyPercent=45 trigger concurrent mark
// G1 phases:
// 1. Young-Only — minor GC on Eden/Survivor regions (STW, short)
// 2. Concurrent Marking — mark live objects (concurrent with app)
// 3. Mixed GC — collect young + some old regions (STW, short)
// 4. Full GC — fallback if concurrent GC can't keep up (long STW)
// Analysing G1 logs
// [GC pause (G1 Evacuation Pause) (young) 512M->128M(1024M) 50ms]
// ^type ^young ^before ^after ^pause
// Common G1 issues:
// • Humongous objects (>50% region size) go to Old Gen directly
// • To Region evacuation failure → Full GC (avoid with adequate heap)ZGC and GC Best Practices
ZGC (production since Java 15) achieves sub-millisecond pauses regardless of heap size by doing almost all work concurrently. Load barriers redirect pointer accesses during relocation. Ideal for latency-sensitive applications with large heaps (hundreds of GB).
Shenandoah (Red Hat) is similar — concurrent evacuation, also sub-millisecond.
// ZGC flags
// -XX:+UseZGC (production Java 15+)
// -XX:SoftMaxHeapSize=28g soft limit (ZGC uses more headroom)
// -XX:+ZGenerational generational ZGC (Java 21, faster)
// Memory leak prevention checklist:
// ✓ Close streams/connections in try-with-resources
// ✓ Remove listeners/observers when done
// ✓ Use WeakReference/SoftReference for caches
// ✓ Avoid static collections that grow unboundedly
// ✓ Beware of ThreadLocal — always call remove()
// ✓ Inner class holds reference to outer — use static inner
// Soft and Weak references
import java.lang.ref.*;
// SoftReference — GC clears it only when memory pressure is high (good for caches)
SoftReference<byte[]> softCache = new SoftReference<>(new byte[1024 * 1024]);
// WeakReference — GC clears it at next GC cycle (good for canonicalising maps)
WeakReference<User> weakUser = new WeakReference<>(user);
User u = weakUser.get(); // may be null if GC ran
if (u != null) { /* use u */ }Interactive Visualization
Key Points to Remember
- Generational hypothesis: most objects die young — GC exploits this with Eden/Survivor/Old.
- Minor GC collects Young Gen (fast); Full GC collects everything (slow — minimize it).
- G1GC: region-based, meets pause time target, default since Java 9.
- ZGC: sub-millisecond pauses, concurrent relocation, ideal for latency-critical large heaps.
- Common memory leaks: unclosed resources, static collections, ThreadLocal without remove(), inner class references.
Practice Garbage Collection in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhat is the generational hypothesis and why does GC exploit it?
What is the difference between Minor GC and Full GC?
How does G1GC achieve predictable pause times?
What is the difference between SoftReference and WeakReference?
How would you diagnose and fix a memory leak in a Java application?
Ask Aria about Garbage Collection
Your personal AI tutor — ask anything about this concept