Java-Speicherverwaltung
Hinweis
Die Pläne Basic, Standard und Enterprise gelten ab Mitte März 2025 als veraltet und werden über einen Zeitraum von drei Jahren eingestellt. Es wird empfohlen, auf Azure Container Apps umzustellen. Weitere Informationen finden Sie in der Ankündigung zur Einstellung von Azure Spring Apps.
Der Plan Standardverbrauch und dediziert gilt ab dem 30. September 2024 als veraltet und wird nach sechs Monaten vollständig eingestellt. Es wird empfohlen, auf Azure Container Apps umzustellen. Weitere Informationen finden Sie unter Migrieren des Plans „Standardverbrauch und dediziert“ von Azure Spring Apps zu Azure Container Apps.
Dieser Artikel gilt für:✅ Basic/Standard ❎ Enterprise
In diesem Artikel werden verschiedene Konzepte im Zusammenhang mit der Java-Speicherverwaltung beschrieben, um Ihnen das Verständnis des Verhaltens von Java-Anwendungen zu erleichtern, die in Azure Spring Apps gehostet werden.
Java-Speichermodell
Der Arbeitsspeicher einer Java-Anwendung weist mehrere Bereiche auf, und es gibt verschiedene Möglichkeiten, diese aufzuteilen. In diesem Artikel wird Java-Arbeitsspeicher als unterteilt in Heapspeicher, Nicht-Heapspeicher und direkten Speicher behandelt.
Heapspeicher
Heapspeicher speichert alle Klasseninstanzen und Arrays. Jeder virtuelle Java-Computer (JVM) verfügt nur über einen Heapbereich, der von Threads gemeinsam genutzt wird.
Spring Boot Actuator kann den Wert des Heapspeichers beobachten. Spring Boot Actuator erfasst den Heapwert als Teil von jvm.memory.used/committed/max
. Weitere Informationen finden Sie im Abschnitt jvm.memory.used/commit/max von Tools zum Behandeln von Arbeitsspeicherproblemen.
Heapspeicher wird in neue Generation und alte Generation unterteilt. Diese Begriffe werden in der folgenden Liste zusammen mit verwandten Begriffen beschrieben.
Neue Generation: Alle neuen Objekte werden in Speicher der neuen Generation zugeordnet und altern darin.
- Eden-Speicher: Neue Objekte werden im Eden-Speicher zugeordnet.
- Survivor-Speicher („Überlebende“): Objekte werden von aus dem Eden- in den Survivor-Speicher verschoben, nachdem sie einen Garbage Collection-Zyklus überstanden haben. Der Survivor-Speicher lässt sich in zwei Bereiche unterteilen: s1 und s2.
Alte Generation: Auch als Tenured-Speicher („Unkündbar“) bezeichnet. Objekte, die längere Zeit im Survivor-Speicher verbracht haben, werden in Speicher der alten Generation verschoben.
Vor Java 8 war auch ein weiterer Abschnitt namens permanente Generation Teil des Heaps. Ab Java 8 wurde die permanente Generation durch Metaspace im Nicht-Heapspeicher ersetzt.
Nicht-Heapspeicher
Nicht-Heapspeicher wird in die folgenden Bereiche aufgeteilt:
Der Teil des Nicht-Heapspeichers, der die permanente Generation (oder permGen) ab Java 8 ersetzt hat. Spring Boot Actuator beobachtet diesen Abschnitt und erfasst ihn als Teil von
jvm.memory.used/committed/max
. Mit anderen Worten:jvm.memory.used/committed/max
ist die Summe aus dem Heapspeicher und dem früheren permGen-Bereich des Nicht-Heapspeichers. Die ehemalige permanente Generation besteht aus den folgenden Bereichen:- Metaspace, worin die Klassendefinitionen gespeichert werden, die von Klassenladeprogrammen geladen werden.
- Komprimierter Klassenspeicher, der für komprimierte Klassenzeiger gedacht ist.
- Codecache, der nativen Code speichert, der von JIT kompiliert wurde.
Sonstiger Arbeitsspeicher wie der Threadstapel, der nicht von Spring Boot Actuator beobachtet wird.
Direkter Speicher
Der direkte Speicher ist nativer Arbeitsspeicher, der von java.nio.DirectByteBuffer
zugeordnet und in Drittanbieterbibliotheken wie nio und gzip verwendet wird.
Der Spring Boot Actuator beobachtet den Wert des direkten Speichers nicht.
Das folgende Diagramm fasst das Java-Speichermodell zusammen, das im vorherigen Abschnitt beschrieben wurde.
Java Garbage Collection
Es gibt drei Begriffe im Zusammenhang mit der Java Garbage Collection (GC): „Minor GC“, „Major GC“ und „Full GC“. Diese Begriffe sind in der JVM-Spezifikation nicht eindeutig definiert. Hier betrachten wir „Major GC“ und „Full GC“ als äquivalent.
„Minor GC“ wird ausgeführt, wenn der Eden-Speicher voll ist. Sie entfernt alle inaktiven (dead) Objekte in der jungen Generation und verschiebt aktive (live) Objekte aus dem Eden-Speicher nach s1 des Survivor-Speichers, oder von s1 nach s2.
Full GC oder Major GC führt die Garbage Collection im gesamten Heap aus. Full GC kann auch Bereiche wie Metaspace und direkten Speicher sammeln, die nur von Full GC bereinigt werden können.
Die maximale Heapgröße beeinflusst die Häufigkeit der geringfügigen GC und der vollständigen GC. Die maximale Größe des Metaspace und des direkten Speichers beeinflussen Full GC.
Wenn Sie die maximale Heapgröße auf einen niedrigeren Wert festlegen, tritt die Garbage Collection häufiger auf, wodurch die App etwas langsamer, die Speicherauslastung aber besser begrenzt wird. Wenn Sie die maximale Heapgröße auf einen höheren Wert festlegen, tritt die Garbage Collection seltener auf, was das Risiko für „Nicht genügend Arbeitsspeicher“-Zustände (Out-of-Memory, OOM) erhöhen kann. Weitere Informationen finden Sie im Abschnitt Typen von „Nicht genügende Arbeitsspeicher“-Problemen (OOM) unter Probleme durch Neustarts von Apps, die durch „Nicht genügend Arbeitsspeicher“-Probleme verursacht werden.
Metaspace und direkter Speicher können nur von Full GC gesammelt werden. Wenn Metaspace oder direkter Speicher voll sind, erfolgt eine Full GC.
Java-Speicherkonfigurationen
In den folgenden Abschnitten werden wichtige Aspekte der Java-Speicherkonfiguration beschrieben.
Java-Containerisierung
Anwendungen in Azure Spring Apps werden in Containerumgebungen ausgeführt. Weitere Informationen finden Sie unter Containerisieren Ihrer Java-Anwendungen.
Wichtige JVM-Optionen
Sie können die maximale Größe jedes Bereichs des Arbeitsspeichers mithilfe von JVM-Optionen konfigurieren. Sie können JVM-Optionen mithilfe von Azure CLI-Befehlen oder über das Azure-Portal festlegen. Weitere Informationen finden Sie im Abschnitt Ändern von Konfigurationen zum Beheben von Problemen unter Tools zum Behandeln von Arbeitsspeicherproblemen.
In der folgenden Liste werden die JVM-Optionen beschrieben:
Konfiguration der Heapgröße
-Xms
legt die anfängliche Heapgröße als absoluten Wert fest.-Xmx
legt die maximale Heapgröße als absoluten Wert fest.-XX:InitialRAMPercentage
legt die anfängliche Heapgröße als Prozentsatz der Heapgröße/Größe des App-Arbeitsspeichers fest.-XX:MaxRAMPercentage
legt die maximale Heapgröße als Prozentsatz der Heapgröße/Größe des App-Arbeitsspeichers fest.
Konfiguration der Größe des direkten Speichers
-XX:MaxDirectMemorySize
legt die maximale Größe des direkten Speichers als absoluten Wert fest. Weitere Informationen finden Sie in der Oracle-Dokumentation unter MaxDirectMemorySize.
Konfiguration der Metaspace-Größe
-XX:MaxMetaspaceSize
legt die maximale Größe des Metaspace als absoluten Wert fest.
Standardmäßige maximale Speichergröße
In den folgenden Abschnitten wird beschrieben, wie standardmäßige maximale Speichergrößen festgelegt werden.
Standardmäßige maximale Heapgröße
Azure Spring Apps legt die standardmäßige maximale Heapspeichergröße auf ca. 50 %–80 % des App-Arbeitsspeichers für Java-Apps fest. Insbesondere verwendet Azure Spring Apps die folgenden Einstellungen:
- Wenn der App-Arbeitsspeicher < 1 GB ist, beträgt die standardmäßige maximale Heapgröße 50 % des App-Arbeitsspeichers.
- Wenn 1 GB <= App-Arbeitsspeicher < 2 GB zutrifft, beträgt die standardmäßige maximale Heapgröße 60 % des App-Arbeitsspeichers.
- Wenn 2 GB <= App-Arbeitsspeicher < 3 GB zutrifft, beträgt die standardmäßige maximale Heapgröße 70 % des App-Arbeitsspeichers.
- Wenn 3 GB <= App-Arbeitsspeicher zutrifft, beträgt die standardmäßige maximale Heapgröße 80 % des App-Arbeitsspeichers.
Standardmäßige maximale Größe des direkten Speichers
Wenn die maximale Größe des direkten Speichers nicht mit JVM-Optionen festgelegt wird, legt JVM automatisch die maximale Größe des direkten Speichers auf den von Runtime.getRuntime.maxMemory() zurückgegebenen Wert fest. Dieser Wert entspricht ungefähr der maximalen Heapspeichergröße. Weitere Informationen finden Sie in der JDK 8-Datei „VM.java“.
Layout der Arbeitsspeicherauslastung
Die Heapgröße wird von Ihrem Durchsatz beeinflusst. Grundsätzlich können Sie bei der Konfiguration die standardmäßige maximale Heapgröße beibehalten, was noch angemessenen Arbeitsspeicher für andere Bereiche übrig lässt.
Die Größe des Metaspace hängt von der Komplexität Ihres Codes ab, z. B. der Anzahl der Klassen.
Die Größe des direkten Speichers hängt von Ihrem Durchsatz und Ihrer Verwendung von Drittanbieterbibliotheken wie nio und gzip ab.
In der folgenden Liste wird ein typisches Beispiel für ein Arbeitsspeicherlayout für 2-GB-Apps beschrieben. Sie können diese Liste als Referenz verwenden, um Ihre Speichergrößeneinstellungen zu konfigurieren.
- Gesamter Arbeitsspeicher (2048M)
- Heapspeicher: Xmx ist 1433,6M (70 % des Gesamtspeichers). Der Referenzwert der täglichen Speicherauslastung beträgt 1200M.
- Junge Generation
- Survivor-Speicher (S0, S1)
- Eden-Speicher
- Alte Generation
- Junge Generation
- Nicht-Heapspeicher
- Beobachteter Teil (beobachtet von Spring Boot Actuator)
- Metaspace: der Referenzwert für die tägliche Auslastung beträgt 50M–256M
- Codecache
- Komprimierter Klassenspeicher
- Nicht beobachteter Teil (nicht beobachtet von Spring Boot Actuator): der Referenzwert für die tägliche Auslastung beträgt 150M–250M.
- Threadstapel
- GC, interne Symbole und andere
- Beobachteter Teil (beobachtet von Spring Boot Actuator)
- Direkter Speicher: der Referenzwert für die tägliche Auslastung beträgt 10M–200M.
Das folgende Diagramm zeigt dieselben Informationen. Zahlen in Grau sind die Referenzwerte für die tägliche Speicherauslastung.
Insgesamt sollten Sie bei der Konfiguration der maximalen Speichergrößen die Auslastung jedes einzelnen Bereichs im Arbeitsspeicher berücksichtigen, wobei die Summe aller maximalen Größen den verfügbaren Gesamtarbeitsspeicher nicht überschreiten sollte.
Java OOM
OOM bedeutet, dass die Anwendung nicht genügend Arbeitsspeicher hat (Out-of-Memory). Dabei gibt es zwei verschiedene Konzepte: Container OOM und JVM OOM. Weitere Informationen finden Sie unter App-Neustartprobleme wegen „Nicht genügend Arbeitsspeicher“-Problemen.