JVM Incident Runbook
Dieses Runbook verhindert Aktionismus während eines Incidents. Zuerst werden Symptom, Zeitfenster und betroffene Instanzen gesichert. Erst danach folgt eine Änderung. Ein Neustart kann stabilisieren, vernichtet aber flüchtige Evidenz; deshalb vorher nach Möglichkeit JFR, Thread Dumps und relevante Metriken sichern.
1. Lage eingrenzen
- Seit wann besteht das Problem und welches Deployment, Flag oder Traffic-Muster liegt davor?
- Sind alle Instanzen betroffen oder nur ein Pod, eine Zone oder eine Mandantengruppe?
- Was ist das primäre Symptom: Latenz, Durchsatz, CPU, Speicher, Fehler, Restarts oder Queue-Wachstum?
- Endete die JVM mit
OutOfMemoryErroroder der Container mit OOMKilled?
2. Evidenz vor Änderungen sichern
| Verdacht | Sofortdaten | Vertiefung |
|---|---|---|
| CPU | Process CPU, Load Average, Runnable Threads | JFR Execution Samples oder async-profiler |
| Allocation/GC | Heap nach GC, Rate, Pausen, Frequenz | JFR Allocation Samples, GC Log, Heap Dump |
| Locks | Blocked Threads, Deadlocks | Zwei Thread Dumps mit Zeitabstand |
| I/O | niedrige CPU, hohe Latenz, Waiting Threads | Traces, Pool-Metriken, Remote-Latenzen |
| Classloading | Loaded Classes, Metaspace-Trend | jcmd VM.classloader_stats |
| Container | RSS gegen Limit, Heap und Non-Heap | NMT, Direct Buffers, Thread-Stacks |
3. Heap und Container trennen
-Xmx ist nicht der Prozessverbrauch. Hinzu kommen Metaspace, Code Cache, Direct Buffers, Thread-Stacks, GC-Strukturen und native Bibliotheken. Ist der Heap moderat, aber RSS nahe am Limit, ist eine reine Heap-Vergrößerung kontraproduktiv.
Container-Limit
├─ Java Heap (-Xmx)
├─ Metaspace und Code Cache
├─ Direct Buffers
├─ Thread-Anzahl × Stack-Größe
├─ GC- und JVM-Strukturen
└─ native Bibliotheken / unbekannte Retention
4. GC-Ereignisse lesen
Eine Pause allein beweist kein GC-Problem. Prüfe Häufigkeit, Dauer, zurückgewonnenen Speicher und Live Set nach GC gemeinsam. Häufige Young GCs mit guter Rückgewinnung können normal sein. Steigt der Heap nach vollständigen Zyklen weiter, ist Retention wahrscheinlicher.
java -Xlog:gc*,safepoint:file=gc.log:time,uptime,level,tags ...
jcmd <pid> GC.heap_info
jcmd <pid> GC.class_histogram
5. Hypothese und Gegenbeweis
Formuliere prüfbar: „Die Latenz steigt, weil eine hohe Allokationsrate den Heap über 85 Prozent hält.“ Gegenbeweis: Bleibt die Latenz bei sinkender Rate hoch oder liegt die Zeit in Remote-Aufrufen, ist die Hypothese unvollständig.
6. Änderung absichern
- Nur eine relevante Variable pro Vergleich ändern.
- Vorher und nachher denselben Workload verwenden.
- Median und Perzentile statt einer Einzelmessung vergleichen.
- Neben Latenz auch Fehler, Durchsatz, CPU, Speicher und GC beobachten.
- Rollback-Kriterium und erwarteten Effekt vorab festhalten.
7. Abschlussnotiz
Dokumentiere Timeline, Evidenz, verworfene Hypothesen, Ursache, Änderung und Messvergleich. Dauerhafte Verbesserungen sind häufig klare Ownership, begrenzte Caches, Backpressure oder reduzierte Allokationen – nicht ein zufälliger Satz JVM-Flags.