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

  1. Mark — Identify live (reachable) objects starting from GC roots
  2. Sweep — Reclaim memory from unreachable objects
  3. 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 = -Xmx in production
  • Monitor GC logs and pause times
  • Fix memory leaks before tuning GC parameters
  • Use try-with-resources to prevent resource leaks