Anatomie des JVM-Speichers: Heap verstehen.
Heap ist ein Speicherbereich, in dem alle Objekte Ihrer Anwendung gespeichert werden. Er ist in mehrere Bereiche unterteilt:
Heap-Struktur:
Junge Generation:
Eden Space — Hier werden neue Objekte erstellt
Survivor Space (S0 und S1) — Objekte, die die erste Müllabfuhr überlebt haben
Old Generation (alte Generation):
Langlebige Objekte, die mehrere Garbage-Collection-Zyklen überlebt haben
Metaspace (ersetzt PermGen in Java 8+):
Klassenmetadaten, Konstanten
Wie stelle ich die Heap-Größe ein?
# Minimale und maximale Heap-Größe festlegen
java -Xms2g -Xmx4g -jar myapp.jar
# -Xms - anfängliche Heap-Größe (2 GB)
# -Xmx - maximale Heap-Größe (4 GB)Wichtige Regel: Xms und Xmx sollten in der Produktion identisch sein, um eine Größenänderung des Heap während der Ausführung zu vermeiden.
Wie berechnet man die richtige Größe?
Zunächst können Sie die folgende Formel verwenden:
Heap Size = (Peak Live Data Size) × (2-4)Wobei Peak Live Data Size die maximale Menge an "lebenden" Objekten nach Full GC ist.
Garbage Collection: Auswahl und Konfiguration
GC (Garbage Collector) entfernt automatisch nicht verwendete Objekte aus dem Speicher. Es gibt verschiedene Arten von Garbage Collectors, jeder mit seinen eigenen Besonderheiten.
Haupttypen von GC:
1. Serial GC (für kleine Anwendungen)
java -XX:+UseSerialGC -jar myapp.jarWann zu verwenden: Single-Thread-Anwendungen, Heap < 100 MB
2. Parallel GC (Standard in Java 8)
java -XX:+UseParallelGC -jar myapp.jarWann zu verwenden: Anwendungen, bei denen die Durchsatzleistung wichtig ist, können Pausen tolerieren
3. G1GC (in den meisten Fällen empfohlen)
java -XX:+UseG1GC -jar myapp.jarWann zu verwenden: Heap > 4 GB, vorhersehbare Pausen erforderlich
Einstellen der Zielpausenzeit:
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar myapp.jar4. ZGC und Shenandoah (für Anwendungen mit geringer Latenz)
# ZGC (Java 11+, produktionsbereit mit Java 15)
java -XX:+UseZGC -jar myapp.jar
# Shenandoah
java -XX:+UseShenandoahGC -jar myapp.jarWann zu verwenden: Minimale Pausen (< 10 ms), großer Heap sind kritisch
GC-Überwachung in Protokollen
Aktivieren Sie detaillierte GC-Protokolle:
java -Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100m \
-XX:+UseG1GC \
-jar myapp.jarProfiling: Engpässe finden
Die Profilerstellung hilft Ihnen zu verstehen, wo Ihre Anwendung Zeit und Speicher verbraucht.
1. Verwenden Sie JVisualVM (kostenlos)
JVisualVM ist im JDK enthalten und ermöglicht:
CPU- und Speichernutzung in Echtzeit überwachen
Heap-Dumps aufnehmen
Analysieren Sie die Ströme
# Starten Sie die Anwendung mit JMX
java -Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-jar myapp.jarVerbinden Sie sich dann über VisualVM mit localhost:9010.
2. Heap-Dump-Analyse
Wenn ein OutOfMemoryError auftritt, ist es hilfreich, einen Speicherauszug zu erhalten:
# Automatischer Dump bei OOM
java -XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/tmp/heapdump.hprof \
-jar myapp.jar
# Oder erstellen Sie manuell
jmap -dump:live,format=b,file=heap.bin <PID>Analysieren Sie den Dump in Eclipse MAT oder VisualVM, suchen Sie nach:
Objekte, die den meisten Speicherplatz belegen
Speicherlecks (memory leaks)
Unerwartet große Sammlungen
3. Async Profiler (production-ready)
Für die Produktion eignet sich async-profiler — es verlangsamt die Anwendung nicht:
# Herunterladen
wget https://github.com/jvm-profiling-tools/async-profiler/releases/latest/download/async-profiler-2.9-linux-x64.tar.gz
# Profiling für 60 Sekunden starten
./profiler.sh -d 60 -f flamegraph.html <PID>Das Ergebnis ist ein Flamegraph, in dem die "heißen" Methoden sichtbar sind.
4. Überwachung von Metriken
Verwenden Sie Micrometer + Prometheus + Grafana:
// Spring Boot exportiert JVM-Metriken automatisch
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}Fügen Sie application.properties hinzu:
management.endpoints.web.exposure.include=prometheus,health,metrics
management.metrics.export.prometheus.enabled=true
Typische Probleme und ihre Lösungen
Problem 1: Häufige Full GC
Symptome: Die App „friert“ für Sekunden ein
Lösung:
# Erhöhen Sie den Heap oder konfigurieren Sie die junge Generation
java -Xms4g -Xmx4g \
-XX:NewRatio=2 \
-XX:+UseG1GC \
-jar myapp.jarProblem 2: OutOfMemoryError: Java Heap-Speicher
Lösung:
Erhöhen Sie
-XmxFinden Sie Speicherlecks durch Heap-Dump
Optimieren Sie den Code (entfernen Sie unnötige Sammlungen)
Problem 3: OutOfMemoryError: Metaspace
Lösung:
# Vergrößern Sie den Metaspace
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -jar myapp.jarProblem 4: Hohe CPU-Auslastung durch GC
Lösung:
Wechseln Sie zu einem effizienteren GC (G1, ZGC)
Erhöhen Sie den Heap
Optimieren Sie die Erstellung von Objekten im Code
Checkliste für die Produktionsvorbereitung
1. Speichereinstellungen
java -Xms4g -Xmx4g \ # Fester Heap
-XX:MetaspaceSize=256m \ # Metaspace
-XX:MaxMetaspaceSize=512m \
-XX:+UseG1GC \ # G1 GC
-XX:MaxGCPauseMillis=200 \ # Zielzeit der Pause
-XX:+HeapDumpOnOutOfMemoryError \ # Dump bei OOM
-XX:HeapDumpPath=/var/log/app \
-jar myapp.jar2. GC-Protokollierung
-Xlog:gc*:file=/var/log/app/gc.log:time,uptime:filecount=5,filesize=100m3. JMX zur Überwachung
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.authenticate=true \
-Dcom.sun.management.jmxremote.ssl=true \
-Dcom.sun.management.jmxremote.password.file=/etc/jmx.password4. Containerisierung (Docker)
JVM erkennt automatisch Containergrenzen mit Java 10+:
FROM openjdk:17-slim
# JVM erkennt die Limits automatisch
ENV JAVA_OPTS="-XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-XX:+UseG1GC"
CMD java $JAVA_OPTS -jar app.jar# docker-compose.yml
services:
app:
image: myapp:latest
deploy:
resources:
limits:
memory: 2G5. Überwachung in der Produktion
Achten Sie unbedingt auf:
Heap usage (verwendeter Speicher)
GC frequency (Häufigkeit der Versammlungen)
GC pause time (Pausendauer)
Thread count (Anzahl der Ströme)
CPU usage
Beispiel für eine fertige Konfiguration
Für eine typische Spring Boot-Anwendung:
#!/bin/bash
java -Xms2g -Xmx2g \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=256m \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/myapp/heapdump.hprof \
-Xlog:gc*:file=/var/log/myapp/gc.log:time,uptime:filecount=10,filesize=100m \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-jar myapp.jarZusätzliche Tools
GCEasy (gceasy.io) — Online-Analyse von GC-Protokollen
FastThread (fastthread.io) — Analyse von Thread-Dumps
JProfiler - kommerzieller Profiler
YourKit - ein weiterer großartiger kommerzieller Profiler
Arthas - Alibaba-Tool zur Laufzeitdiagnose
Schlussfolgerungen
Die JVM-Optimierung ist ein iterativer Prozess. Beginnen Sie mit dem Verständnis der grundlegenden Konzepte (Heap, GC, Profiling), verwenden Sie die richtigen Tools für die Diagnose und optimieren Sie schrittweise für Ihre spezifische Auslastung.
Denken Sie daran: Vorzeitige Optimierung ist die Wurzel allen Übels. Erst messen, dann optimieren!
War es hilfreich?
Schließen Sie sich an Kodik — Bildungsplattform für Entwickler!
Hier finden Sie strukturierte Kurse, praktische Aufgaben und aktuelle Materialien zu Java, Leistungsoptimierung und Produktionsvorbereitung.
Und wir haben auch steil Telegram-Kanal mit einer freundlichen Community, wo erfahrene Entwickler ihr Wissen teilen, bei der Lösung von Problemen helfen und Best Practices diskutieren. Machen Sie mit!
