Garbage Collection
Garbage Collection (GC) automatically reclaims memory occupied by objects that are no longer reachable. The JVM determines reachability and frees heap space without programmer intervention.
How GC Works
- Mark — Identify live (reachable) objects starting from GC roots
- Sweep — Reclaim memory from unreachable objects
- Compact — (Optional) Move live objects to reduce fragmentation
GC Roots
Objects reachable from roots are considered live:
- Local variables and parameters on thread stacks
- Active Java threads
- Static fields of loaded classes
- JNI references
Person p = new Person("Alice"); // reachable via stack reference
p = null; // object now unreachable — eligible for GC
Generational Hypothesis
Most objects die young. The heap is divided into generations:
| Generation | Contents | GC Frequency |
|---|---|---|
| Young (Minor GC) | New objects | Frequent, fast |
| Old (Major/Full GC) | Long-lived objects | Infrequent, slower |
Minor GC — Cleans young generation (Stop-the-World, usually milliseconds) Full GC — Cleans entire heap (Stop-the-World, can pause seconds)
Garbage Collectors
| Collector | Flag | Best For |
|---|---|---|
| Serial | -XX:+UseSerialGC |
Small apps, single core |
| Parallel (Throughput) | -XX:+UseParallelGC |
Batch processing, max throughput |
| G1 (default since Java 9) | -XX:+UseG1GC |
General purpose, balanced |
| ZGC | -XX:+UseZGC |
Low latency, large heaps (Java 15+) |
| Shenandoah | -XX:+UseShenandoahGC |
Low latency (OpenJDK) |
G1 GC (Default)
Divides heap into equal-sized regions. Targets predictable pause times:
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xms2g -Xmx2g MyApp
ZGC
Ultra-low pause times (< 1 ms), supports heaps up to 16 TB:
java -XX:+UseZGC -Xms4g -Xmx4g MyApp
Monitoring GC
# Print GC details to console
java -Xlog:gc* -Xms512m -Xmx512m MyApp
# Generate heap dump on OOM
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof MyApp
Programmatic:
Runtime runtime = Runtime.getRuntime();
long total = runtime.totalMemory();
long free = runtime.freeMemory();
long used = total - free;
System.out.printf("Used: %d MB%n", used / (1024 * 1024));
Common GC Tuning Flags
| Flag | Purpose |
|---|---|
-Xms |
Initial heap size |
-Xmx |
Maximum heap size |
-Xss |
Thread stack size |
-XX:MaxMetaspaceSize |
Metaspace limit |
-XX:MaxGCPauseMillis |
Target max pause (G1) |
-XX:+UseStringDeduplication |
Deduplicate strings (G1) |
Rule of thumb: Set -Xms equal to -Xmx in production to avoid heap resizing.
Memory Leaks in Java
Java has GC, but memory leaks still occur when objects remain reachable unintentionally:
// ❌ Static collection grows forever
private static List<Object> cache = new ArrayList<>();
// ❌ Unclosed resources
InputStream is = new FileInputStream("file.txt"); // not closed
// ❌ Listener not removed
button.addListener(listener); // listener never removed
Object Finalization
finalize() is deprecated (Java 9) and removed (Java 18). Use try-with-resources and Cleaner/PhantomReference instead.
Best Practices
- Let G1 be the default unless you have specific latency or throughput requirements
- Set
-Xms=-Xmxin production - Monitor GC logs and pause times
- Fix memory leaks before tuning GC parameters
- Use try-with-resources to prevent resource leaks