Garbage collector tuning selects the right GC algorithm and configures it for your application’s latency and throughput requirements.

GC Algorithms

Collector Flag Best for Pause time
G1 (default Java 9+) -XX:+UseG1GC General purpose, balanced Low–moderate
ZGC -XX:+UseZGC Large heaps, low latency <1ms
Shenandoah -XX:+UseShenandoahGC Low latency, concurrent <10ms
Parallel -XX:+UseParallelGC Throughput, batch jobs Moderate
Serial -XX:+UseSerialGC Small apps, single core High

G1 GC Configuration

Default since Java 9. Good starting point for most applications:

  -XX:+UseG1GC
-XX:MaxGCPauseMillis=200        # target max pause
-XX:G1HeapRegionSize=16m        # region size (auto by default)
-XX:InitiatingHeapOccupancyPercent=45  # start concurrent marking
-XX:G1ReservePercent=10
  

ZGC (Java 15+ production-ready)

For applications requiring sub-millisecond pauses:

  -XX:+UseZGC
-Xmx16g
# ZGC works best with larger heaps (8g+)
  

Characteristics:

  • Pauses typically <1ms regardless of heap size
  • Concurrent compaction
  • No stop-the-world for most phases

GC Logging

  # Java 9+ unified logging
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags:filecount=5,filesize=20m

# Key log events
# Pause Young (Normal) — minor GC
# Pause Full — major GC (bad, investigate)
# Concurrent Cycle — G1 concurrent marking
  

Reading GC Logs

  [2024-01-15T10:30:00.123+0000] GC(42) Pause Young (Normal) 512M->128M(2048M) 15.234ms
  
Field Meaning
GC(42) GC cycle number
Pause Young Minor GC in young generation
512M->128M Heap before → after
(2048M) Current heap capacity
15.234ms Pause duration

Warning signs:

  • Frequent Full GC → heap too small or memory leak
  • Pause times exceeding target → tune or switch collector
  • Allocation Failure → heap pressure

Choosing a Collector

  Latency-sensitive (APIs, trading)?
  └─ Yes → ZGC or Shenandoah (heap > 4g)
  └─ No → Throughput priority?
       └─ Yes → Parallel GC
       └─ No → G1 GC (default, good balance)
  

Tuning Workflow

  1. Baseline — run with defaults, enable GC logging
  2. Measure — collect pause times, throughput under load
  3. Identify — frequent Full GC? Long pauses? High allocation rate?
  4. Adjust — heap size, collector, specific flags
  5. Validate — re-test under production-like load

jstat Monitoring

  # GC utilization summary every 2 seconds
jstat -gcutil <pid> 2000

  S0     S1     E      O      M     CCS    YGC   YGCT    FGC    FGCT     GCT
  0.00  45.23  67.89  32.10  95.12  91.30   142   1.234     3   0.456   1.690

# O (Old gen) consistently > 85% → increase heap or tune
# FGC growing rapidly → memory leak or undersized old gen
  

Best Practices

  • Start with G1 defaults — only tune when metrics show problems
  • Use ZGC for latency-critical services with heaps > 4GB
  • Always enable GC logging in production
  • Set MaxGCPauseMillis as a target, not a guarantee
  • Never disable GC (-Xnoclassgc etc.) without expert knowledge