1. The Hook (The "Byte-Sized" Intro)
- In a Nutshell: GC tuning = adjust JVM flags for throughput/latency goals.
- Key flags: -Xms (initial heap), -Xmx (max heap), -Xmn (young gen size), -XX:MaxGCPauseMillis (pause goal).
- GC logging: -Xlog:gc (Java 9+) tracks GC events. 3 tuning goals: (1) Throughput (app time vs GC time), (2) Latency (pause times), (3) Footprint (heap size).
- Trade-off: Can't optimize all three! Monitoring: GC logs, JVisualVM, GC Viewer.
- Golden rule: Set -Xms = -Xmx (avoid resizing overhead)!
Think of car tuning. GC flags = tuning knobs (engine power vs fuel efficiency). Throughput = horsepower (speed). Latency = smoothness (no jerks). Footprint = fuel tank size (memory). Can't max all three—race car (power) vs hybrid (efficiency)!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- Throughput: Delivery truck (max cargo, don't care about speed)
- Latency: Ambulance (fast response, minimize delays)
- Footprint: Compact car (small, efficient)
3. Technical Mastery (The "Deep Dive")
Essential JVM Flags
| Flag | Purpose | Example |
|---|---|---|
| -Xms | Initial heap size | -Xms2g |
| -Xmx | Maximum heap size | -Xmx4g |
| -Xmn | Young gen size | -Xmn1g |
| -XX:NewRatio | Old/Young ratio | -XX:NewRatio=2 (Old=2×Young) |
| -XX:MaxGCPauseMillis | Pause time goal | -XX:MaxGCPauseMillis=200 |
| -XX:GCTimeRatio | Throughput goal | -XX:GCTimeRatio=99 (99% app, 1% GC) |
4. Interactive & Applied Code
bash
# ========================================
# BASIC TUNING
# ========================================
# Set heap size (most important!)
java -Xms4g -Xmx4g -jar app.jar
# ✅ Best practice: Xms = Xmx (avoid resizing)
# Young generation size
java -Xms4g -Xmx4g -Xmn1g -jar app.jar
# Rule of thumb: Young = 25-40% of heap
# ========================================
# G1 GC TUNING
# ========================================
# Pause time goal
java -XX:+UseG1GC \
-Xms8g -Xmx8g \
-XX:MaxGCPauseMillis=200 \
-jar app.jar
# G1 tries to meet 200ms pause goal
# Concurrent GC threads
java -XX:+UseG1GC \
-XX:ConcGCThreads=2 \
-jar app.jar
# ========================================
# PARALLEL GC TUNING (Throughput)
# ========================================
# Max throughput
java -XX:+UseParallelGC \
-Xms4g -Xmx4g \
-XX:GCTimeRatio=99 \
-jar app.jar
# 99% app time, 1% GC time
# Parallel GC threads
java -XX:+UseParallelGC \
-XX:ParallelGCThreads=8 \
-jar app.jar
# ========================================
# ZGC TUNING (Low Latency)
# ========================================
# ZGC for large heap
java -XX:+UseZGC \
-Xms16g -Xmx16g \
-jar app.jar
# Pauses <10ms
# ========================================
# GC LOGGING (Java 9+)
# ========================================
# Basic GC logging
java -Xlog:gc -jar app.jar
# Detailed GC logging
java -Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100M \
-jar app.jar
# ========================================
# GC LOGGING (Java 8)
# ========================================
# Java 8 style
java -XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-Xloggc:gc.log \
-jar app.jarJava code for GC monitoring:
java
import java.lang.management.*;
import java.util.*;
public class GCMonitoringDemo {
public static void main(String[] args) throws Exception {
demonstrateGCMonitoring();
demonstrateMemoryUsage();
}
// Monitor GC activity
static void demonstrateGCMonitoring() {
List<GarbageCollectorMXBean> gcBeans =
ManagementFactory.getGarbageCollectorMXBeans();
System.out.println("=== GC MONITORING ===");
for (GarbageCollectorMXBean gcBean : gcBeans) {
System.out.println("\nGC: " + gcBean.getName());
System.out.println(" Collections: " + gcBean.getCollectionCount());
System.out.println(" Time: " + gcBean.getCollectionTime() + "ms");
System.out.println(" Pools: " + Arrays.toString(gcBean.getMemoryPoolNames()));
}
}
// Monitor memory usage
static void demonstrateMemoryUsage() {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
System.out.println("\n=== MEMORY USAGE ===");
// Heap memory
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
System.out.println("Heap:");
System.out.println(" Init: " + heapUsage.getInit() / 1024 / 1024 + " MB");
System.out.println(" Used: " + heapUsage.getUsed() / 1024 / 1024 + " MB");
System.out.println(" Committed: " + heapUsage.getCommitted() / 1024 / 1024 + " MB");
System.out.println(" Max: " + heapUsage.getMax() / 1024 / 1024 + " MB");
// Non-heap memory
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
System.out.println("\nNon-Heap (Metaspace):");
System.out.println(" Used: " + nonHeapUsage.getUsed() / 1024 / 1024 + " MB");
}
}5. The Comparison & Decision Layer
| Goal | Optimize For | Flags |
|---|---|---|
| Throughput | App time | -XX:+UseParallelGC, -XX:GCTimeRatio=99 |
| Latency | Pause time | -XX:+UseG1GC, -XX:MaxGCPauseMillis=50 |
| Footprint | Heap size | Smaller -Xmx, Serial GC |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "Why set -Xms equal to -Xmx?" Answer: Avoid heap resizing overhead!
bash
# ❌ BAD: Heap grows dynamically
java -Xms512m -Xmx4g -jar app.jar
# JVM resizes heap during runtime (expensive pauses!)
# ✅ GOOD: Fixed heap size
java -Xms4g -Xmx4g -jar app.jar
# No resizing, predictable performancePro-Tips:
- Tuning order:
text
1. Choose GC (G1 default is good)
2. Set heap size (-Xms/-Xmx)
3. Set goals (pause time or throughput)
4. Monitor and iterate
5. Don't over-tune!- GC log analysis:
bash
# Parse GC logs
# Look for:
# - Pause times (should be < goal)
# - GC frequency (too frequent = undersized heap)
# - Promotion rate (young → old)
# - Full GC events (should be rare)