26.3 GC Tuning and Monitoring
Effective GC tuning requires understanding performance metrics and using appropriate monitoring tools.
GC Performance Metrics
// Understanding GC Metrics
public class GCMetrics {
public static void printPerformanceMetrics() {
System.out.println("=== GC PERFORMANCE METRICS ===");
System.out.println("\n--- PRIMARY METRICS ---");
System.out.println("\n1. THROUGHPUT");
System.out.println("Definition: % of time NOT spent in GC");
System.out.println("Formula: (Total time - GC time) / Total time");
System.out.println("Example: 95% throughput = 5% time in GC");
System.out.println("\nGood values:");
System.out.println(" - >99%: Excellent");
System.out.println(" - 95-99%: Good");
System.out.println(" - <95%: May need tuning");
System.out.println("\n2. PAUSE TIME (LATENCY)");
System.out.println("Definition: Duration of stop-the-world pauses");
System.out.println("Metrics:");
System.out.println(" - Average pause time");
System.out.println(" - Maximum pause time");
System.out.println(" - P99 pause time (99th percentile)");
System.out.println("\nTarget values (depends on SLA):");
System.out.println(" - Interactive apps: <100ms");
System.out.println(" - Trading systems: <1ms");
System.out.println(" - Batch processing: <10s acceptable");
System.out.println("\n3. MEMORY FOOTPRINT");
System.out.println("Definition: Total memory used by application + GC");
System.out.println("Includes:");
System.out.println(" - Heap memory");
System.out.println(" - Metaspace");
System.out.println(" - GC data structures (remembered sets, marking bitmaps)");
System.out.println(" - Native memory");
System.out.println("\n--- SECONDARY METRICS ---");
System.out.println("\n4. ALLOCATION RATE");
System.out.println("Definition: MB/sec of object allocation");
System.out.println("High allocation → More frequent GC");
System.out.println("Monitor: Track to identify allocation hotspots");
System.out.println("\n5. PROMOTION RATE");
System.out.println("Definition: MB/sec of objects promoted to Old Gen");
System.out.println("High promotion → Old Gen fills faster");
System.out.println("Problem: Premature promotion pollutes Old Gen");
System.out.println("\n6. GC FREQUENCY");
System.out.println("Minor GC: Every few seconds (normal)");
System.out.println("Major GC: Every few minutes to hours");
System.out.println("Full GC: Should be rare (or never)");
System.out.println("\n--- TRADE-OFFS ---");
System.out.println("Cannot optimize all metrics simultaneously:");
System.out.println(" • Low pause time ↔ High throughput");
System.out.println(" • Low footprint ↔ High throughput");
System.out.println(" • Low pause time ↔ Low footprint");
}
}
GC Logging
// GC Logging Configuration
public class GCLogging {
public static void printLoggingOptions() {
System.out.println("=== GC LOGGING (JAVA 9+) ===");
System.out.println("\n--- UNIFIED LOGGING (-Xlog) ---");
System.out.println("\nBasic syntax:");
System.out.println(" -Xlog:<tags>:<output>:<decorators>:<level>");
System.out.println("\n--- COMMON CONFIGURATIONS ---");
System.out.println("\n1. Basic GC logging:");
System.out.println(" -Xlog:gc*:file=gc.log");
System.out.println(" Logs all GC events to gc.log");
System.out.println("\n2. Detailed GC logging:");
System.out.println(" -Xlog:gc*:file=gc.log:time,uptime,level,tags");
System.out.println(" Includes timestamps, uptime, log level, tags");
System.out.println("\n3. Separate files by type:");
System.out.println(" -Xlog:gc:file=gc.log");
System.out.println(" -Xlog:gc+heap=debug:file=gc-heap.log");
System.out.println("\n4. Rotating logs:");
System.out.println(" -Xlog:gc*:file=gc.log::filecount=5,filesize=10M");
System.out.println(" Keeps 5 files, 10MB each");
System.out.println("\n5. Console + file:");
System.out.println(" -Xlog:gc*:stdout");
System.out.println(" -Xlog:gc*:file=gc.log");
System.out.println("\n--- G1-SPECIFIC LOGGING ---");
System.out.println(" -Xlog:gc*,gc+ergo*=trace:file=gc.log");
System.out.println(" Includes ergonomic decisions");
System.out.println("\n--- ZGC-SPECIFIC LOGGING ---");
System.out.println(" -Xlog:gc,gc+heap=info:file=gc.log");
System.out.println("\n--- SHENANDOAH-SPECIFIC LOGGING ---");
System.out.println(" -Xlog:gc,gc+ergo=info:file=gc.log");
}
public static void printLogAnalysis() {
System.out.println("\n=== ANALYZING GC LOGS ===");
System.out.println("\n--- KEY INFORMATION TO EXTRACT ---");
System.out.println("\n1. GC Pause Times");
System.out.println(" - Look for 'Pause' events");
System.out.println(" - Track min/max/average");
System.out.println(" - Identify outliers");
System.out.println("\n2. GC Frequency");
System.out.println(" - Count Minor/Major GC events");
System.out.println(" - Calculate time between GCs");
System.out.println(" - Identify patterns");
System.out.println("\n3. Heap Utilization");
System.out.println(" - Before/after heap sizes");
System.out.println(" - Young Gen vs Old Gen usage");
System.out.println(" - Trends over time");
System.out.println("\n4. Allocation/Promotion Rates");
System.out.println(" - Eden growth rate");
System.out.println(" - Old Gen growth rate");
System.out.println(" - Identify memory leaks");
System.out.println("\n--- LOG ANALYSIS TOOLS ---");
System.out.println("1. GCViewer");
System.out.println(" - Visual analysis of GC logs");
System.out.println(" - Charts and statistics");
System.out.println("\n2. GCEasy (gceasy.io)");
System.out.println(" - Online log analyzer");
System.out.println(" - Recommendations");
System.out.println("\n3. Censum (jClarity)");
System.out.println(" - Advanced GC analysis");
System.out.println(" - Production monitoring");
}
}
Command-Line Monitoring Tools
// jstat - GC Statistics
public class JStatMonitoring {
public static void printJStatUsage() {
System.out.println("=== JSTAT - GC STATISTICS ===");
System.out.println("\n--- BASIC USAGE ---");
System.out.println("jstat -gc <pid> <interval> <count>");
System.out.println(" <pid>: Java process ID");
System.out.println(" <interval>: Sampling interval (ms)");
System.out.println(" <count>: Number of samples");
System.out.println("\n--- COMMON OPTIONS ---");
System.out.println("\n1. Overall GC statistics:");
System.out.println(" jstat -gc 12345 1000 10");
System.out.println(" Sample every 1 second, 10 times");
System.out.println(" Shows heap sizes, GC counts, GC times");
System.out.println("\n2. GC capacity:");
System.out.println(" jstat -gccapacity 12345");
System.out.println(" Min/max sizes of generations");
System.out.println("\n3. GC cause:");
System.out.println(" jstat -gccause 12345");
System.out.println(" Last GC cause and current GC");
System.out.println("\n4. New generation statistics:");
System.out.println(" jstat -gcnew 12345 1000");
System.out.println(" Eden, Survivor spaces");
System.out.println("\n5. Old generation statistics:");
System.out.println(" jstat -gcold 12345 1000");
System.out.println(" Old Gen and Metaspace");
System.out.println("\n6. GC utilization:");
System.out.println(" jstat -gcutil 12345 1000");
System.out.println(" Percentage utilization");
System.out.println("\n--- INTERPRETING JSTAT OUTPUT ---");
System.out.println("\nKey columns (jstat -gc):");
System.out.println(" S0C, S1C: Survivor 0/1 capacity (KB)");
System.out.println(" S0U, S1U: Survivor 0/1 used (KB)");
System.out.println(" EC: Eden capacity (KB)");
System.out.println(" EU: Eden used (KB)");
System.out.println(" OC: Old Gen capacity (KB)");
System.out.println(" OU: Old Gen used (KB)");
System.out.println(" MC: Metaspace capacity (KB)");
System.out.println(" MU: Metaspace used (KB)");
System.out.println(" YGC: Young GC count");
System.out.println(" YGCT: Young GC time (seconds)");
System.out.println(" FGC: Full GC count");
System.out.println(" FGCT: Full GC time (seconds)");
System.out.println(" GCT: Total GC time (seconds)");
System.out.println("\n--- WHAT TO LOOK FOR ---");
System.out.println("✓ Steady YGC increase: Normal");
System.out.println("⚠ Frequent FGC: Problem (tune heap or fix leaks)");
System.out.println("⚠ OU approaching OC: Old Gen filling up");
System.out.println("⚠ YGCT/FGCT increasing rapidly: GC taking more time");
}
}
// jcmd - JVM Diagnostic Commands
class JCmdMonitoring {
public static void printJCmdUsage() {
System.out.println("\n=== JCMD - JVM DIAGNOSTIC COMMANDS ===");
System.out.println("\n--- GC-RELATED COMMANDS ---");
System.out.println("\n1. List available commands:");
System.out.println(" jcmd <pid> help");
System.out.println("\n2. Trigger GC:");
System.out.println(" jcmd <pid> GC.run");
System.out.println(" Forces full garbage collection");
System.out.println("\n3. GC heap info:");
System.out.println(" jcmd <pid> GC.heap_info");
System.out.println(" Current heap usage and configuration");
System.out.println("\n4. Class histogram:");
System.out.println(" jcmd <pid> GC.class_histogram");
System.out.println(" Objects by class, count, size");
System.out.println("\n5. Heap dump:");
System.out.println(" jcmd <pid> GC.heap_dump /tmp/heap.hprof");
System.out.println(" Capture heap snapshot");
System.out.println("\n6. VM flags:");
System.out.println(" jcmd <pid> VM.flags");
System.out.println(" All JVM flags (including GC)");
System.out.println("\n7. VM uptime:");
System.out.println(" jcmd <pid> VM.uptime");
System.out.println("\n8. System properties:");
System.out.println(" jcmd <pid> VM.system_properties");
}
}
// jmap - Heap Diagnostics
class JMapMonitoring {
public static void printJMapUsage() {
System.out.println("\n=== JMAP - HEAP DIAGNOSTICS ===");
System.out.println("\n--- COMMON COMMANDS ---");
System.out.println("\n1. Heap summary:");
System.out.println(" jmap -heap <pid>");
System.out.println(" GC algorithm, heap config, memory usage");
System.out.println("\n2. Heap histogram:");
System.out.println(" jmap -histo <pid>");
System.out.println(" Object count by class");
System.out.println(" jmap -histo:live <pid>");
System.out.println(" Only live objects (triggers GC)");
System.out.println("\n3. Heap dump:");
System.out.println(" jmap -dump:live,format=b,file=heap.hprof <pid>");
System.out.println(" Binary heap dump for analysis");
System.out.println("\n4. Finalization queue:");
System.out.println(" jmap -finalizerinfo <pid>");
System.out.println(" Objects awaiting finalization");
System.out.println("\n--- ANALYZING HEAP DUMPS ---");
System.out.println("Tools:");
System.out.println(" • Eclipse MAT (Memory Analyzer Tool)");
System.out.println(" • VisualVM");
System.out.println(" • JProfiler");
System.out.println(" • YourKit");
System.out.println("\n⚠ WARNING");
System.out.println(" - Heap dumps can be large (GB+)");
System.out.println(" - May pause application briefly");
System.out.println(" - Use in production with caution");
}
}
GUI Monitoring Tools
// VisualVM
public class VisualVMMonitoring {
public static void printVisualVMFeatures() {
System.out.println("=== VISUALVM ===");
System.out.println("\n--- OVERVIEW ---");
System.out.println("Free visual monitoring tool");
System.out.println("Part of JDK (until Java 9), standalone after");
System.out.println("Download: visualvm.github.io");
System.out.println("\n--- GC MONITORING FEATURES ---");
System.out.println("\n1. Real-time heap monitoring");
System.out.println(" - Heap size over time");
System.out.println(" - Used vs allocated");
System.out.println(" - Generation breakdown");
System.out.println("\n2. GC activity visualization");
System.out.println(" - GC events timeline");
System.out.println(" - Pause times");
System.out.println(" - Frequency");
System.out.println("\n3. Heap dump analysis");
System.out.println(" - Load .hprof files");
System.out.println(" - Largest objects");
System.out.println(" - Dominator tree");
System.out.println(" - OQL (Object Query Language)");
System.out.println("\n4. Profiling");
System.out.println(" - CPU profiling");
System.out.println(" - Memory allocation profiling");
System.out.println(" - Identify hotspots");
System.out.println("\n--- PLUGINS ---");
System.out.println(" • Visual GC: Real-time GC visualization");
System.out.println(" • BTrace: Dynamic tracing");
System.out.println(" • Threads: Thread analysis");
}
}
// JConsole
class JConsoleMonitoring {
public static void printJConsoleFeatures() {
System.out.println("\n=== JCONSOLE ===");
System.out.println("\n--- OVERVIEW ---");
System.out.println("Built-in JMX monitoring tool");
System.out.println("Ships with JDK");
System.out.println("Command: jconsole");
System.out.println("\n--- GC MONITORING FEATURES ---");
System.out.println("\n1. Memory tab");
System.out.println(" - Heap memory graph");
System.out.println(" - Non-heap memory graph");
System.out.println(" - Memory pools breakdown");
System.out.println(" - Perform GC button");
System.out.println("\n2. MBeans tab");
System.out.println(" - java.lang:type=Memory");
System.out.println(" - java.lang:type=GarbageCollector,name=*");
System.out.println(" - java.lang:type=MemoryPool,name=*");
System.out.println("\n3. VM Summary tab");
System.out.println(" - JVM arguments");
System.out.println(" - GC algorithm");
System.out.println(" - Heap configuration");
System.out.println("\n--- REMOTE MONITORING ---");
System.out.println("Enable JMX:");
System.out.println(" -Dcom.sun.management.jmxremote");
System.out.println(" -Dcom.sun.management.jmxremote.port=9999");
System.out.println(" -Dcom.sun.management.jmxremote.authenticate=false");
System.out.println(" -Dcom.sun.management.jmxremote.ssl=false");
}
}
Java Flight Recorder (JFR)
// JFR for GC Analysis
public class JFRMonitoring {
public static void printJFRUsage() {
System.out.println("=== JAVA FLIGHT RECORDER (JFR) ===");
System.out.println("\n--- OVERVIEW ---");
System.out.println("Low-overhead profiling framework");
System.out.println("Built into JDK (Java 11+, free in Java 11+)");
System.out.println("Records JVM events for analysis");
System.out.println("\n--- STARTING JFR ---");
System.out.println("\n1. Command-line (at startup):");
System.out.println(" java -XX:StartFlightRecording=");
System.out.println(" duration=60s,");
System.out.println(" filename=recording.jfr MyApp");
System.out.println("\n2. jcmd (running JVM):");
System.out.println(" jcmd <pid> JFR.start duration=60s filename=rec.jfr");
System.out.println("\n3. Dump recording:");
System.out.println(" jcmd <pid> JFR.dump filename=dump.jfr");
System.out.println("\n4. Stop recording:");
System.out.println(" jcmd <pid> JFR.stop");
System.out.println("\n--- GC-RELATED JFR EVENTS ---");
System.out.println("\n1. jdk.GarbageCollection");
System.out.println(" - All GC events");
System.out.println(" - Duration, type, cause");
System.out.println("\n2. jdk.G1GarbageCollection");
System.out.println(" - G1-specific events");
System.out.println("\n3. jdk.ZGarbageCollection");
System.out.println(" - ZGC-specific events");
System.out.println("\n4. jdk.GCHeapSummary");
System.out.println(" - Heap state before/after GC");
System.out.println("\n5. jdk.ObjectAllocationInNewTLAB");
System.out.println(" - TLAB allocations");
System.out.println("\n6. jdk.ObjectAllocationOutsideTLAB");
System.out.println(" - Large object allocations");
System.out.println("\n--- ANALYZING JFR FILES ---");
System.out.println("\n1. Java Mission Control (JMC)");
System.out.println(" - Visual analysis tool");
System.out.println(" - Download: jdk.java.net/jmc");
System.out.println(" - Open .jfr files");
System.out.println("\n2. Command-line:");
System.out.println(" jfr print recording.jfr");
System.out.println(" jfr summary recording.jfr");
System.out.println("\n--- JMC ANALYSIS FEATURES ---");
System.out.println(" • GC timeline and pause times");
System.out.println(" • Memory allocation hotspots");
System.out.println(" • CPU usage during GC");
System.out.println(" • Automated analysis (warnings/suggestions)");
}
}
Tuning Parameters
// Common GC Tuning Flags
public class GCTuningParameters {
public static void printHeapSizingFlags() {
System.out.println("=== HEAP SIZING FLAGS ===");
System.out.println("\n--- BASIC HEAP SIZE ---");
System.out.println("-Xms<size>: Initial heap size");
System.out.println(" Example: -Xms4g");
System.out.println("-Xmx<size>: Maximum heap size");
System.out.println(" Example: -Xmx8g");
System.out.println("⭐ Best practice: Set -Xms equal to -Xmx");
System.out.println(" Avoids heap resizing overhead");
System.out.println("\n--- GENERATION SIZING ---");
System.out.println("-XX:NewSize=<size>: Initial Young Gen size");
System.out.println("-XX:MaxNewSize=<size>: Maximum Young Gen size");
System.out.println("-XX:NewRatio=<n>: Old/Young size ratio");
System.out.println(" Example: -XX:NewRatio=2 → Old is 2x Young");
System.out.println("-XX:SurvivorRatio=<n>: Eden/Survivor ratio");
System.out.println(" Example: -XX:SurvivorRatio=8 → Eden is 8x Survivor");
System.out.println("\n--- METASPACE ---");
System.out.println("-XX:MetaspaceSize=<size>: Initial metaspace");
System.out.println("-XX:MaxMetaspaceSize=<size>: Maximum metaspace");
System.out.println(" Default: Unlimited (grows as needed)");
System.out.println(" Recommendation: Set limit to prevent runaway growth");
}
public static void printG1TuningFlags() {
System.out.println("\n=== G1 TUNING FLAGS ===");
System.out.println("\n-XX:MaxGCPauseMillis=<ms>");
System.out.println(" Pause time goal (not guarantee)");
System.out.println(" Default: 200ms");
System.out.println(" Example: -XX:MaxGCPauseMillis=100");
System.out.println("\n-XX:G1HeapRegionSize=<size>");
System.out.println(" Region size (1MB - 32MB, power of 2)");
System.out.println(" Auto-calculated by default");
System.out.println(" Example: -XX:G1HeapRegionSize=16m");
System.out.println("\n-XX:InitiatingHeapOccupancyPercent=<n>");
System.out.println(" Heap % to trigger concurrent marking");
System.out.println(" Default: 45");
System.out.println(" Lower → More frequent marking, less Full GC risk");
System.out.println(" Example: -XX:InitiatingHeapOccupancyPercent=35");
System.out.println("\n-XX:G1NewSizePercent=<n>");
System.out.println(" Minimum Young Gen % of heap");
System.out.println(" Default: 5");
System.out.println("\n-XX:G1MaxNewSizePercent=<n>");
System.out.println(" Maximum Young Gen % of heap");
System.out.println(" Default: 60");
System.out.println("\n-XX:G1MixedGCCountTarget=<n>");
System.out.println(" Target number of mixed GCs after marking");
System.out.println(" Default: 8");
System.out.println(" Lower → More aggressive Old Gen collection");
System.out.println("\n-XX:ConcGCThreads=<n>");
System.out.println(" Concurrent marking threads");
System.out.println(" Default: ~ParallelGCThreads/4");
}
public static void printZGCTuningFlags() {
System.out.println("\n=== ZGC TUNING FLAGS ===");
System.out.println("\n-XX:ConcGCThreads=<n>");
System.out.println(" Concurrent GC threads");
System.out.println(" Default: 12.5% of cores");
System.out.println(" Increase for better throughput");
System.out.println("\n-XX:ZCollectionInterval=<seconds>");
System.out.println(" Minimum time between GC cycles");
System.out.println(" Proactive GC (prevents allocation stalls)");
System.out.println(" Example: -XX:ZCollectionInterval=60");
System.out.println("\n-XX:ZUncommitDelay=<seconds>");
System.out.println(" Time before uncommitting unused memory");
System.out.println(" Default: 300 (5 minutes)");
System.out.println(" 0 = disabled");
System.out.println("\n-XX:+ZGenerational (Java 21+)");
System.out.println(" Enable generational ZGC");
System.out.println(" Improves throughput significantly");
}
public static void printCommonFlags() {
System.out.println("\n=== COMMON GC FLAGS ===");
System.out.println("\n-XX:ParallelGCThreads=<n>");
System.out.println(" STW GC threads");
System.out.println(" Default: # of CPU cores (up to 8), then scaled");
System.out.println("\n-XX:+DisableExplicitGC");
System.out.println(" Ignore System.gc() calls");
System.out.println(" Recommended for production");
System.out.println("\n-XX:MaxTenuringThreshold=<n>");
System.out.println(" Survivor age before promotion");
System.out.println(" Default: 15 (G1: dynamic)");
System.out.println(" Range: 0-15");
System.out.println("\n-XX:+AlwaysPreTouch");
System.out.println(" Touch all heap pages at startup");
System.out.println(" Prevents lazy allocation delays");
System.out.println(" Increases startup time");
}
}
Best Practices
- Enable GC logging: Always log GC in production.
- Monitor continuously: Use JFR or monitoring tools.
- Set -Xms = -Xmx: Avoid heap resizing.
- Start with defaults: Tune only when needed.
- Measure before tuning: Establish baseline metrics.
- Change one parameter at a time: Isolate effects.
- Test under load: Reproduce production traffic.
- Monitor allocation rate: High allocation = frequent GC.
- Watch for Full GC: Should be rare; investigate causes.
- Use JFR for profiling: Low overhead, comprehensive data.