{}const=>[]async()letfn</>var
EntwicklungPraxis

JVM-Optimierung: von einer langsamen Anwendung zur Produktionsumgebung in 30 Minuten

In diesem ausführlichen Leitfaden werden wir uns ansehen, wie man die JVM richtig konfiguriert, einen Garbage Collector auswählt, eine Anwendung profiliert und sie für echte Lasten vorbereitet. Praktische Beispiele, vorgefertigte Konfigurationen und eine Checkliste für die Produktion – alles, was ein unerfahrener Entwickler braucht.

К

Kodik

Autor

5 Min. Lesezeit

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.

🔥 100.000+ Schüler sind bereits bei uns

Genug Theorie gelesen?
Zeit zu coden!

Kodik — eine App, in der du durch Praxis programmieren lernst. KI-Mentor, interaktive Lektionen, echte Projekte.

🤖 KI 24/7
🎓 Zertifikate
💰 Kostenlos
🚀 Jetzt starten
Heute beigetreten

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.jar

Wann zu verwenden: Single-Thread-Anwendungen, Heap < 100 MB

2. Parallel GC (Standard in Java 8)

java -XX:+UseParallelGC -jar myapp.jar

Wann 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.jar

Wann zu verwenden: Heap > 4 GB, vorhersehbare Pausen erforderlich

Einstellen der Zielpausenzeit:

java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar myapp.jar

4. 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.jar

Wann 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.jar

Profiling: 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.jar

Verbinden 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.jar

Problem 2: OutOfMemoryError: Java Heap-Speicher

Lösung:

  1. Erhöhen Sie -Xmx

  2. Finden Sie Speicherlecks durch Heap-Dump

  3. 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.jar

Problem 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.jar

2. GC-Protokollierung

-Xlog:gc*:file=/var/log/app/gc.log:time,uptime:filecount=5,filesize=100m

3. 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.password

4. 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: 2G

5. Ü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.jar

Zusä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!

🎯Hör auf zu zögern

Artikel gefallen?
Zeit zum Üben!

Bei Kodik liest du nicht nur — du schreibst sofort Code. Theorie + Praxis = echte Skills.

Sofortige Praxis
🧠KI erklärt Code
🏆Zertifikat

Keine Registrierung • Keine Karte