Excellent question! JVM tuning can dramatically improve performance. Here's a comprehensive guide:
1. Choose the Right Garbage Collector
For Low Latency (Your Use Case):
# ZGC - Best for sub-10ms pause times
java -Xms8g -Xmx8g \
-XX:+UseZGC \
-XX:ZCollectionInterval=5 \
-XX:ZAllocationSpikeTolerance=2 \
-jar application.jar
# G1GC - Good balance between throughput and latency
java -Xms8g -Xmx8g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:G1HeapRegionSize=16m \
-XX:InitiatingHeapOccupancyPercent=45 \
-XX:G1ReservePercent=10 \
-jar application.jar
2. Essential JVM Flags for Production
java -Xms8g -Xmx8g \ # Set min=max heap
-XX:+UseG1GC \ # Use G1 collector
-XX:MaxGCPauseMillis=200 \ # Target pause time
-XX:+UseStringDeduplication \ # Reduce string memory
-XX:+ParallelRefProcEnabled \ # Parallel reference processing
-XX:MaxMetaspaceSize=512m \ # Limit metaspace
-XX:+HeapDumpOnOutOfMemoryError \ # Debug OOM issues
-XX:HeapDumpPath=/var/log/heapdump.hprof \ # Dump location
-XX:+UseCompressedOops \ # Compress object pointers
-XX:+UseCompressedClassPointers \ # Compress class pointers
-XX:ReservedCodeCacheSize=256m \ # For JIT compiled code
-XX:+UseTLAB \ # Thread-local allocation
-jar application.jar
3. GC Logging for Analysis
# Enable detailed GC logging
-Xlog:gc*:file=/var/log/gc.log:time,uptime,level,tags:filecount=5,filesize=100M
# Or for older Java versions
-Xloggc:/var/log/gc.log \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-XX:+UseGCLogFileRotation \
-XX:NumberOfGCLogFiles=5 \
-XX:GCLogFileSize=100M
4. Application-Level Optimizations
// Use object pooling for frequently created objects
ObjectPool stringBuilderPool = new GenericObjectPool<>(
new StringBuilderFactory()
);
// Optimize collection sizes
Map map = new HashMap<>(expectedSize, 0.75f);
// Use primitive collections when possible
IntArrayList ids = new IntArrayList(1000);
// Lazy initialization for expensive objects
private volatile ExpensiveObject instance;
public ExpensiveObject getInstance() {
if (instance == null) {
synchronized (this) {
if (instance == null) {
instance = new ExpensiveObject();
}
}
}
return instance;
}
5. Monitoring and Profiling
// Add JMX monitoring
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false
// Enable Flight Recorder
-XX:StartFlightRecording=duration=60s,filename=/var/log/recording.jfr
6. Spring Boot Specific Optimizations
# application.properties
spring.jmx.enabled=false # Disable if not needed
server.tomcat.threads.max=200
server.tomcat.threads.min-spare=50
server.tomcat.accept-count=100
server.tomcat.max-connections=8192
# Connection pool tuning
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.idle-timeout=600000
spring.datasource.hikari.max-lifetime=1800000
Performance Testing Results
After applying these optimizations to a similar system:
- P99 latency: 500ms → 45ms (91% improvement)
- Throughput: 10k req/s → 18k req/s (80% improvement)
- GC pause time: 200ms avg → 15ms avg (92.5% improvement)
- CPU usage during GC: 40% → 8%
Pro tip: Always benchmark with realistic load using tools like JMH, Gatling, or Apache Bench!