JVM-Performance: vom Symptom zur belastbaren Diagnose
Dieses Lab behandelt JVM-Performance nicht als Sammlung magischer Flags. Ausgangspunkt ist ein Produktionssymptom: Die p95-Latenz steigt, der Prozess benötigt mehr Speicher und einzelne Instanzen werden neu gestartet. Aus einem korrelierten Snapshot entstehen überprüfbare Hypothesen, konkrete nächste Messungen und ein Vorher-/Nachher-Vergleich.
Lernziele
- Heap-Druck von Container- und Native-Memory-Druck unterscheiden.
- CPU-, Allocation-, Lock-, I/O- und Classloading-Probleme anhand von Evidenz trennen.
- GC-Ereignisse interpretieren, ohne jede Pause vorschnell zum Hauptproblem zu erklären.
- lokale Zeitmessung einordnen und wissen, wann JFR, JMH oder async-profiler nötig sind.
- eine Änderung nur dann als Verbesserung bewerten, wenn dieselben Messgrößen erneut erhoben wurden.
Der Diagnosepfad
- Symptom: Latenz, Durchsatz, Fehlerquote, Restarts und Zeitpunkt festhalten.
- Snapshot: Heap, Non-Heap, Threads, GC, Classloading, CPU und Container gemeinsam erfassen.
- Hypothese: Eine primäre Ursache benennen, aber Unsicherheit sichtbar lassen.
- Vertiefung: Mit JFR, Heap Dump, Thread Dumps, NMT oder Traces gezielt nachmessen.
- Änderung: Im Szenario wird unbegrenzte Retention durch einen begrenzten LRU-Cache ersetzt.
- Vergleich: Heap-Auslastung, Allokationsrate und p95-Latenz werden erneut bewertet.
Zentrale Klassen
| Klasse | Verantwortung | Lernpunkt |
|---|---|---|
| JvmDiagnosticSnapshot | Korreliert Messwerte eines Zeitpunkts. | Einzelmetriken ohne Zeitbezug führen zu falschen Schlüssen. |
| JvmIncidentClassifier | Erzeugt Diagnose, Evidenz und nächste Schritte. | Heuristiken liefern eine Hypothese, keinen Beweis. |
| DiagnosticScenarioFactory | Definiert reproduzierbare Vorher-/Nachher-Snapshots. | Optimierung braucht vergleichbare Bedingungen. |
| GcLogParser | Liest Lehrformat und Java Unified Logging. | Parser und Diagnose bleiben getrennt testbar. |
| BenchmarkReport | Berechnet Median und p95. | Eine Einzelmessung ist kein Benchmark. |
| ThreadStateSampler | Zählt Zustände und erkennt Deadlocks. | Viele Threads bedeuten nicht automatisch hohe Parallelität. |
Ausführen
mvn clean test
java -cp target/classes com.example.enterprise.run8a.Run8ATestRunner
java -cp target/classes com.example.enterprise.run8a.Run8ADemo
Messgrenzen
Die Schwellenwerte des Klassifikators sind bewusst lesbare Lehrwerte, keine universellen Produktionsgrenzen. Ein echtes System benötigt Baselines pro Workload, Zeitreihen und die Korrelation mit Deployments und fachlichem Traffic. System.nanoTime() eignet sich für lokale Instrumentierung, aber nicht allein für belastbare Microbenchmarks: JIT-Warm-up, Dead-Code-Elimination, CPU-Frequenz, GC und Ausreißer müssen kontrolliert werden. Dafür ist JMH vorgesehen.