Dlaczego performance tuning JVM jest kluczowy?
Domyślne ustawienia JVM są zaprojektowane dla przeciętnych aplikacji, ale każdy system ma unikalne charakterystyki. Odpowiednie strojenie JVM może poprawić wydajność o 20-80% i znacznie zredukować koszty infrastruktury. W środowiskach produkcyjnych nawet kilkuprocentowa poprawa może przełożyć się na tysiące dolarów oszczędności rocznie.
Co się nauczysz:
- Jak analizować wydajność JVM za pomocą wbudowanych narzędzi
- Optymalizacja heap memory i garbage collection
- Tuning JIT compiler dla lepszej wydajności CPU
- Monitorowanie key metrics w środowisku produkcyjnym
- Systematyczne podejście do performance tuning
Anatomia wydajności JVM – key areas
Performance tuning JVM koncentruje się na trzech głównych obszarach:
Obszary optymalizacji JVM
Obszar | Wpływ na wydajność | Kluczowe parametry |
---|---|---|
Heap Memory | Częstotliwość GC, throughput | -Xms, -Xmx, -XX:NewRatio |
Garbage Collector | Latency, pause times | -XX:+UseG1GC, -XX:MaxGCPauseMillis |
JIT Compiler | CPU utilization, warm-up time | -XX:CompileThreshold, -XX:TieredCompilation |
Metaspace | Class loading, memory leaks | -XX:MaxMetaspaceSize |
Narzędzia do diagnozy wydajności
jstat – monitoring garbage collection
Najważniejsze narzędzie do monitorowania GC w czasie rzeczywistym:
# Monitoring GC co 5 sekund jstat -gc [PID] 5s # Przykładowe wyjście: # S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT # 512.0 512.0 0.0 32.0 4608.0 1024.0 11264.0 2048.0 21248.0 20534.3 2560.0 2361.6 10 0.150 2 0.100 0.250 # Interpretacja kolumn: # S0C/S1C - rozmiar survivor spaces # EU - eden space utilization # OU - old generation utilization # YGC/YGCT - young generation collections/time # FGC/FGCT - full GC collections/time
jvisualvm – graficzny profiler
Wbudowane narzędzie graficzne dostępne w JDK:
# Uruchomienie VisualVM jvisualvm # Najważniejsze funkcje: # - Monitor: heap/CPU/threads w czasie rzeczywistym # - Sampler: profiling metod bez instrumentacji # - Profiler: szczegółowa analiza hot spots # - MBeans: zarządzanie JMX beans
Java Flight Recorder (JFR)
Narzędzie low-overhead do szczegółowego profilowania:
# Włączenie JFR dla aplikacji java -XX:+UnlockCommercialFeatures \ -XX:+FlightRecorder \ -XX:StartFlightRecording=duration=60s,filename=myapp.jfr \ MyApplication # Analiza nagrania jmc myapp.jfr # Java Mission Control
Heap Memory Tuning
Sizing heap memory
Podstawowe parametry heap memory:
# Podstawowe ustawienia heap -Xms2g # Initial heap size -Xmx4g # Maximum heap size -XX:NewRatio=3 # Old/Young generation ratio (3:1) -XX:SurvivorRatio=8 # Eden/Survivor ratio (8:1:1) # Przykład dla aplikacji web (4GB RAM dostępne) java -Xms1g -Xmx3g -XX:NewRatio=2 \ -XX:+UseG1GC \ MyWebApplication
Strategia sizing heap
- Zaczynaj konserwatywnie: Xms = 25% RAM, Xmx = 50% RAM
- Monitoruj heap utilization: optimal = 70-80% po full GC
- Dostrajaj stopniowo: zwiększaj heap jeśli utilization > 85%
- Young generation: większy młody heap = rzadsze GC ale dłuższe pauzy
Analiza heap dump
# Generowanie heap dump jcmd [PID] GC.run_finalization jcmd [PID] VM.gc # Force GC przed dump jmap -dump:format=b,file=heapdump.hprof [PID] # Analiza za pomocą Eclipse MAT lub VisualVM # Szukaj: # - Duplicate strings/objects # - Large arrays # - Memory leaks (objects nie GC'd) # - Dominators (obiekty zajmujące najwięcej pamięci)
Garbage Collection Tuning
Wybór garbage collector
GC Algorithm | Best For | Pause Times | Throughput |
---|---|---|---|
Serial GC | Single core, small apps | High | Good for small heap |
Parallel GC | Multi-core, batch processing | Medium-High | High |
CMS GC | Low latency applications | Low | Medium |
G1 GC | Large heap (>4GB), balanced | Predictable | Good |
G1 Garbage Collector tuning
G1GC to najnowocześniejszy collector w Java 8 (będzie domyślny w Java 9):
# Podstawowa konfiguracja G1GC -XX:+UseG1GC -XX:MaxGCPauseMillis=200 # Target pause time -XX:G1HeapRegionSize=16m # Region size (1-32MB) -XX:G1NewSizePercent=20 # Min young gen size -XX:G1MaxNewSizePercent=40 # Max young gen size -XX:G1MixedGCCountTarget=8 # Mixed GC cycles # Przykład konfiguracji dla aplikacji web: java -Xms2g -Xmx8g \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=100 \ -XX:G1HeapRegionSize=16m \ MyWebApp
CMS Garbage Collector dla low-latency
# CMS GC dla aplikacji wymagających niskiej latency -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 # Start CMS at 70% old gen -XX:+UseCMSInitiatingOccupancyOnly -XX:+CMSClassUnloadingEnabled # Clean up perm gen/metaspace # Monitoring CMS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
JIT Compiler Optimization
Tiered Compilation
Java 8 używa tiered compilation domyślnie:
# Tiered compilation levels: # Level 0: interpreter # Level 1: C1 compiler (client) # Level 2: C1 with limited profiling # Level 3: C1 with full profiling # Level 4: C2 compiler (server) - highest optimization # Monitoring JIT compilation -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining # Tuning compilation thresholds -XX:CompileThreshold=10000 # Default method invocation threshold -XX:Tier3CompileThreshold=2000 # C1 compilation threshold -XX:Tier4CompileThreshold=15000 # C2 compilation threshold
Code Cache tuning
# Code cache sizing -XX:InitialCodeCacheSize=64m -XX:ReservedCodeCacheSize=256m -XX:CodeCacheExpansionSize=2m # Monitoring code cache jstat -compiler [PID] # Compilation statistics jcmd [PID] Compiler.codecache # Code cache usage
Monitoring i key metrics
Kluczowe metryki wydajności
Metryka | Target Value | Narzędzie |
---|---|---|
GC Overhead | < 5% total time | jstat, GC logs |
Max Pause Time | < 100ms for web apps | GC logs |
Heap Utilization | 70-80% after full GC | jstat, VisualVM |
Allocation Rate | Depends on app | Flight Recorder |
CPU Usage | < 80% sustained | top, VisualVM |
Thread Count | Stable, no leaks | jstack, VisualVM |
Continuous monitoring setup
# Production monitoring flags -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -Xloggc:/var/log/myapp/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=100M # JMX monitoring -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
Systematyczne podejście do tuning
Performance tuning workflow
- Baseline measurement: Zmierz current performance
- Identify bottlenecks: CPU, memory, GC, IO?
- Hypothesis: Co może być przyczyną problemu?
- Single change: Jedna zmiana na raz
- Measure impact: A/B testing with metrics
- Document results: Co działało, co nie
Common tuning scenarios
• Increase young generation size
• Optimize object pooling
• Review algorithm efficiency
• Increase heap size
• Check for memory leaks
• Tune GC algorithm parameters
• Check GC overhead
• Profile hot methods
• Consider JIT compiler tuning
Production deployment best practices
JVM flags for production
# Recommended production flags (Java 8, web application) java -server \ -Xms4g -Xmx4g \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ -XX:+UnlockExperimentalVMOptions \ -XX:+UseCGroupMemoryLimitForHeap \ -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps \ -Xloggc:gc.log -XX:+UseGCLogFileRotation \ -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=100M \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath=/var/log/heapdumps/ \ -Djava.awt.headless=true \ MyApplication
Tuning powinien być częścią początkowego deployment i potem reaktywny na zmiany w charakterystyce aplikacji. Monitoruj metryki ciągle, ale zmiany wprowadzaj tylko gdy obserwujesz degradację wydajności.
Tak, środowiska powinny mieć proporcjonalne ustawienia. Dev może używać mniejszego heap, ale proporcje i GC algorithm powinny być podobne do produkcji dla lepszego testowania.
G1GC dla aplikacji z heap > 4GB i wymaganiami balanced latency/throughput. CMS dla ultra-low latency applications z mniejszym heap. Parallel GC dla batch processing z focus na throughput.
Szukaj: pause times (powinny być < 100ms dla web apps), frequency (młody GC co kilka sekund jest OK, full GC rzadko), heap utilization po GC (70-80% to optimum). Użyj GCViewer do analizy.
Długie pause times (>1s), frequent full GC (>1/minute), high GC overhead (>10% CPU time), OutOfMemoryError, degrading performance over time, high CPU usage z niskim throughput.
Nie! Zbyt duży heap może powodować długie pause times podczas full GC i gorsze locality of reference. Optimal heap size to balance między frequency a duration of GC.
Używaj -XX:+UseCGroupMemoryLimitForHeap (Java 8u131+) aby JVM respektowała container limits. Ustaw heap na ~70% dostępnej container memory. Monitoruj container memory usage vs JVM heap usage.
🚀 Zadanie dla Ciebie
Stwórz benchmark aplikacji i przetestuj różne konfiguracje GC:
- Baseline: Uruchom aplikację z domyślnymi ustawieniami JVM
- G1GC test: Skonfiguruj G1GC z MaxGCPauseMillis=100ms
- CMS test: Skonfiguruj CMS z CMSInitiatingOccupancyFraction=70
- Heap tuning: Przetestuj różne rozmiary heap (1GB, 2GB, 4GB)
Porównaj metryki: average latency, 95th percentile latency, throughput, GC overhead. Udokumentuj wyniki i wybierz najlepszą konfigurację.
Przydatne zasoby:
- Oracle JVM Tuning Guide
- GC Tuning Documentation
- GCViewer – analiza GC logów
- GCeasy.io – online GC analyzer
Jakie są twoje doświadczenia z tuningiem JVM? Które metryki uważasz za najważniejsze w monitoringu produkcyjnym?