Understanding and tuning garbage collection is crucial for optimal Java application performance. This guide covers GC algorithms, monitoring tools, and tuning strategies.
Key areas covered:
# Serial GC Configuration
-XX:+UseSerialGC
-Xms2g
-Xmx2g
# Memory Generations
Young Generation:
- Eden Space
- Survivor Space 0
- Survivor Space 1
Old Generation:
- Tenured Space
# GC Process
1. Mark live objects
2. Copy surviving objects
3. Compact heap space
# Parallel GC Configuration
-XX:+UseParallelGC
-XX:ParallelGCThreads=4
-XX:MaxGCPauseMillis=200
-XX:GCTimeRatio=99
# Advanced Tuning
-XX:YoungGenerationSizeIncrement=20
-XX:TenuredGenerationSizeIncrement=20
-XX:AdaptiveSizeDecrementScaleFactor=4
# Monitoring
jstat -gcutil $pid 1000
jstat -gccause $pid 1000
# G1 GC Configuration
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:G1HeapRegionSize=16M
-XX:G1ReservePercent=10
-XX:InitiatingHeapOccupancyPercent=45
# Logging Configuration
-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100m
# Advanced Settings
-XX:ConcGCThreads=4
-XX:ParallelGCThreads=8
-XX:G1MixedGCLiveThresholdPercent=90
-XX:G1HeapWastePercent=5
public class G1GCDemo {
// Simulate object allocation patterns
public void allocateObjects() {
List objects = new ArrayList<>();
Random random = new Random();
while (true) {
// Allocate objects of varying sizes
int size = random.nextInt(1024 * 1024);
objects.add(new byte[size]);
// Remove some objects to trigger GC
if (objects.size() > 100) {
objects.subList(0, 50).clear();
}
// Sleep to control allocation rate
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
public static void main(String[] args) {
// JVM arguments for monitoring
// -XX:+UseG1GC
// -XX:+PrintGCDetails
// -XX:+PrintGCTimeStamps
// -Xloggc:gc.log
new G1GCDemo().allocateObjects();
}
}
// Memory allocation example
public class MemoryManagement {
// Large object allocation
private static final int LARGE_OBJECT_SIZE = 1024 * 1024;
public void demonstrateAllocation() {
// Young generation allocation
List youngObjects = new ArrayList<>();
for (int i = 0; i < 100; i++) {
youngObjects.add(new byte[1024]);
}
// Old generation promotion
List oldObjects = new ArrayList<>();
for (int i = 0; i < 10; i++) {
oldObjects.add(new byte[LARGE_OBJECT_SIZE]);
System.gc(); // Force GC for demonstration
}
}
// Memory leak prevention
public class ResourceManager implements AutoCloseable {
private final List resources = new ArrayList<>();
public void allocateResource() {
resources.add(new byte[1024]);
}
@Override
public void close() {
resources.clear();
}
}
public void properResourceManagement() {
try (ResourceManager manager = new ResourceManager()) {
manager.allocateResource();
// Use resources
}
}
}
public class MemoryLeakExamples {
// Common memory leak: Static collections
private static final List
# Common GC Tuning Scenarios
# High-Throughput Configuration
-XX:+UseParallelGC
-XX:ParallelGCThreads=8
-XX:MaxGCPauseMillis=500
-XX:GCTimeRatio=19
-Xms4g
-Xmx4g
# Low-Latency Configuration
-XX:+UseG1GC
-XX:MaxGCPauseMillis=50
-XX:G1HeapRegionSize=16M
-XX:+ParallelRefProcEnabled
-XX:G1RSetUpdatingPauseTimePercent=5
-Xms8g
-Xmx8g
# Container-Optimized Configuration
-XX:+UseContainerSupport
-XX:MaxRAMPercentage=75.0
-XX:InitialRAMPercentage=50.0
-XX:+UseG1GC
-XX:+ExitOnOutOfMemoryError
public class GCMonitoring {
private static final Logger logger =
LoggerFactory.getLogger(GCMonitoring.class);
public void monitorGC() {
List gcBeans =
ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcBean : gcBeans) {
logger.info("GC Name: {}", gcBean.getName());
logger.info("Collection Count: {}",
gcBean.getCollectionCount());
logger.info("Collection Time: {} ms",
gcBean.getCollectionTime());
}
}
public void monitorMemory() {
MemoryMXBean memoryBean =
ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
logger.info("Heap Memory Usage:");
logger.info("Init: {} MB",
heapUsage.getInit() / (1024 * 1024));
logger.info("Used: {} MB",
heapUsage.getUsed() / (1024 * 1024));
logger.info("Committed: {} MB",
heapUsage.getCommitted() / (1024 * 1024));
logger.info("Max: {} MB",
heapUsage.getMax() / (1024 * 1024));
}
}
# OutOfMemoryError Analysis
jmap -dump:format=b,file=heap.hprof $pid
# Thread Dump Analysis
jstack -l $pid > thread_dump.txt
# GC Log Analysis
# Enable GC logging
-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100m
# Heap Analysis
jmap -heap $pid
# Class Histogram
jmap -histo $pid
public class PerformanceAnalysis {
private static final Logger logger =
LoggerFactory.getLogger(PerformanceAnalysis.class);
public void analyzeGCImpact() {
long startTime = System.nanoTime();
long totalGCTime = 0;
for (GarbageCollectorMXBean gc :
ManagementFactory.getGarbageCollectorMXBeans()) {
totalGCTime += gc.getCollectionTime();
}
long uptime =
ManagementFactory.getRuntimeMXBean().getUptime();
double gcTimePercentage =
(double) totalGCTime / uptime * 100;
logger.info("GC Time Percentage: {}%",
String.format("%.2f", gcTimePercentage));
}
public void analyzeMemoryUsage() {
MemoryMXBean memoryBean =
ManagementFactory.getMemoryMXBean();
List memoryPools =
ManagementFactory.getMemoryPoolMXBeans();
for (MemoryPoolMXBean pool : memoryPools) {
MemoryUsage usage = pool.getUsage();
logger.info("Memory Pool: {}", pool.getName());
logger.info("Used: {} MB",
usage.getUsed() / (1024 * 1024));
logger.info("Max: {} MB",
usage.getMax() / (1024 * 1024));
}
}
}
Understanding and properly tuning garbage collection is crucial for Java application performance. By following the guidelines and best practices in this guide, you can effectively manage memory and optimize GC behavior for your specific use case.
Remember to regularly monitor GC performance, analyze patterns, and adjust configurations based on your application's requirements and behavior.