Analyse und Lösung des ansteigenden Speicherverbrauchs von Spring-Boot-Anwendungen in der Docker-Umgebung
Verwandte Tags:1. FAQ zu Fehlern bei docker login, docker push und docker pull
2. So installieren Sie Docker auf Ubuntu 20
Einführung in Spring Boot
Eine einfache Spring-Boot-Anwendung wird fast nur von einem Benutzer genutzt und erreicht bereits 1,2 GB Speicher – das ist problematisch.
Eine einfache Spring-Boot-Anwendung wird fast nur von einem Benutzer genutzt und erreicht bereits 1,2 GB Speicher – das ist problematisch.
Dienststatus
Da es wenige Dienste gab und die Serverressourcen ausreichend waren, wurden viele Dienste ohne JVM-Parameter gestartet (Altlasten). Das Ergebnis ist, dass jeder gestartete Dienst 1,2–2 GB Speicher belegt, obwohl einige Dienste gar nicht so viel benötigen. Wie wird also der JVM-Speicher in Spring Boot konfiguriert, wenn keine JVM-Speicherparameter festgelegt sind?
Wenn Sie ein Spring-Boot-Projekt ausführen und keine JVM-Speicherparameter festlegen, verwendet Spring Boot standardmäßig die eigene Standardkonfigurationsstrategie der JVM. Bei ausreichenden Ressourcen kümmern sich Entwickler meist wenig um Speichereinstellungen. Sobald jedoch Ressourcenknappheit und JVM-Optimierung ins Spiel kommen, müssen Sie die Standard-JVM-Speicherkonfigurationsstrategie verstehen.
Die häufigsten Einstellungen für den JVM-Speicher sind die anfängliche Heap-Größe (-Xms) und der maximale Heap-Speicher (-Xmx). Viele verzichten darauf, diese einzustellen, und verwenden stattdessen die Standardwerte der JVM. Besonders in Entwicklungsumgebungen kann der Speicher schnell überlaufen, wenn viele Microservices gestartet werden.
Die Standard-JVM-Speicherkonfigurationsstrategie von Spring Boot unterscheidet zwei Szenarien: ein Szenario mit großem Speicherbereich und ein Szenario mit kleinem Speicherbereich (weniger als 192 MB).
Am Beispiel von 4 GB Arbeitsspeicher ergeben sich folgende Werte für die anfängliche und maximale Heap-Speichergröße:
Standardmäßig belegt der maximale Heap-Speicher 1/4 des physischen Speichers. Wenn die Anwendung dieses Limit überschreitet, wird eine OutOfMemoryError-Ausnahme ausgelöst. Die anfängliche Heap-Speichergröße beträgt 1/64 des physischen Speichers.
Wenn die Anwendung auf einem Mobiltelefon ausgeführt wird oder der physische Speicher weniger als 192 MB beträgt, gelten folgende Standardwerte für die anfängliche und maximale Heap-Speichergröße der JVM:
Der maximale Heap-Speicher beträgt 1/2 des physischen Speichers und die anfängliche Heap-Speichergröße 1/64 des physischen Speichers, wobei die anfängliche Heap-Speichergröße mindestens 8 MB beträgt.

Wenn der freie Heap-Speicher standardmäßig unter 40 % fällt, vergrößert die JVM den Heap bis zum Maximum von -Xmx; wenn der freie Heap-Speicher über 70 % liegt, verkleinert die JVM den Heap bis zum Minimum von -Xms.
Daher werden auf dem Server -Xms und -Xmx üblicherweise gleichgesetzt, um eine Größenänderung des Heaps nach jedem GC-Vorgang zu vermeiden. Der Heap-Speicher eines Objekts wird durch ein automatisches Speicherverwaltungssystem, den sogenannten Garbage Collector, freigegeben.
Der maximale Heap-Speicher ist die Obergrenze des von der JVM verwendeten Speichers; im tatsächlichen Betrieb wird nur so viel verbraucht wie benötigt. Standardmäßig wird maximal ein Drittel der gesamten Heap-Größe der Young Generation zugewiesen.
Um das ursprüngliche Problem zu lösen: Wenn jedes Programm mit der Standardkonfiguration gestartet wird und mehrere Anwendungen auf einem Server bereitgestellt werden, entsteht ein Speichermangel, der zu einer gewissen Verschwendung führt. Die einfachste Maßnahme besteht darin, beim Ausführen von java -jar die entsprechenden JVM-Speicherparameter hinzuzufügen.
java -Xms64m -Xmx128m -jar xxx.jar
Das Projekt verwendet Docker-Bereitstellung. Sehen wir uns zunächst die ursprüngliche Dockerfile an:
EXPOSE 8061
ADD target/certif-system-2.1.0.jar /certif-system-2.1.0.jar
ENTRYPOINT ["java", "-jar","/certif-system-2.1.0.jar"]
Spring-Boot-Optimierung
JVM-Speicher begrenzen
Spring Boot gibt alle Umgebungsvariablen an die Anwendung weiter – aber JAVA_OPTS richtet sich nicht an die Anwendung, sondern an die Java-Laufzeitumgebung selbst. Daher müssen wir die Variable $JAVA_OPTS verwenden, um exec java aufzurufen. Dies erfordert einige kleine Änderungen an der Dockerfile:
ENTRYPOINT exec java $JAVA_OPTS -jar app.jar
Den docker-run-Befehl ausführen
Dies bedeutet, dass die JAVA_OPTS-Parameterinformationen in der Umgebungsvariable zur Laufzeit durch -e zurückgesetzt werden.
docker run -e JAVA_OPTS='-Xmx1344M -Xms1344M -Xmn448M - XX:MaxMetaspaceSize =192M - XX:MetaspaceSize =192M'
Rolle von java.security.egd
SecureRandom wird in verschiedenen Java-Komponenten häufig verwendet und kann zuverlässig Zufallszahlen generieren. In Szenarien, in denen eine große Anzahl von Zufallszahlen erzeugt wird, sinkt jedoch die Leistung. In diesem Fall können Sie „-Djava.security.egd=file:/dev/./urandom“ verwenden, um den Zufallszahlengenerierungsprozess zu beschleunigen.
Es wird empfohlen, den Zufallszahlengenerator als /dev/./urandom anzugeben, wenn viele Zufallszahlen benötigt werden.
Bitte beachten Sie die vierte Zeile des folgenden Quellcodes für die Ursache des Fehlers. Wenn der Parameter java.security.egd file:/dev/random oder file:/dev/urandom angibt, wird der parameterlose NativeSeedGenerator-Konstruktor aufgerufen, und dieser verwendet standardmäßig file:/dev/random. Der Code von OpenJDK unterscheidet sich vom HotSpot-Code. OpenJDK verwendet diese Variable bei der späteren Zufallszahlengenerierung nicht.
2. So installieren Sie Docker auf Ubuntu 20
Einführung in Spring Boot
Eine einfache Spring-Boot-Anwendung wird fast nur von einem Benutzer genutzt und erreicht bereits 1,2 GB Speicher – das ist problematisch.
Ansteigender Speicherverbrauch bei Spring-Boot-Anwendungen
Eine einfache Spring-Boot-Anwendung wird fast nur von einem Benutzer genutzt und erreicht bereits 1,2 GB Speicher – das ist problematisch.
Dienststatus
Da es wenige Dienste gab und die Serverressourcen ausreichend waren, wurden viele Dienste ohne JVM-Parameter gestartet (Altlasten). Das Ergebnis ist, dass jeder gestartete Dienst 1,2–2 GB Speicher belegt, obwohl einige Dienste gar nicht so viel benötigen. Wie wird also der JVM-Speicher in Spring Boot konfiguriert, wenn keine JVM-Speicherparameter festgelegt sind?
Standardmäßige JVM-Speichereinstellungen in Spring Boot
Wenn Sie ein Spring-Boot-Projekt ausführen und keine JVM-Speicherparameter festlegen, verwendet Spring Boot standardmäßig die eigene Standardkonfigurationsstrategie der JVM. Bei ausreichenden Ressourcen kümmern sich Entwickler meist wenig um Speichereinstellungen. Sobald jedoch Ressourcenknappheit und JVM-Optimierung ins Spiel kommen, müssen Sie die Standard-JVM-Speicherkonfigurationsstrategie verstehen.
Die häufigsten Einstellungen für den JVM-Speicher sind die anfängliche Heap-Größe (-Xms) und der maximale Heap-Speicher (-Xmx). Viele verzichten darauf, diese einzustellen, und verwenden stattdessen die Standardwerte der JVM. Besonders in Entwicklungsumgebungen kann der Speicher schnell überlaufen, wenn viele Microservices gestartet werden.
Die Standard-JVM-Speicherkonfigurationsstrategie von Spring Boot unterscheidet zwei Szenarien: ein Szenario mit großem Speicherbereich und ein Szenario mit kleinem Speicherbereich (weniger als 192 MB).
Am Beispiel von 4 GB Arbeitsspeicher ergeben sich folgende Werte für die anfängliche und maximale Heap-Speichergröße:
Standardmäßig belegt der maximale Heap-Speicher 1/4 des physischen Speichers. Wenn die Anwendung dieses Limit überschreitet, wird eine OutOfMemoryError-Ausnahme ausgelöst. Die anfängliche Heap-Speichergröße beträgt 1/64 des physischen Speichers.
Wenn die Anwendung auf einem Mobiltelefon ausgeführt wird oder der physische Speicher weniger als 192 MB beträgt, gelten folgende Standardwerte für die anfängliche und maximale Heap-Speichergröße der JVM:
Der maximale Heap-Speicher beträgt 1/2 des physischen Speichers und die anfängliche Heap-Speichergröße 1/64 des physischen Speichers, wobei die anfängliche Heap-Speichergröße mindestens 8 MB beträgt.

Wenn der freie Heap-Speicher standardmäßig unter 40 % fällt, vergrößert die JVM den Heap bis zum Maximum von -Xmx; wenn der freie Heap-Speicher über 70 % liegt, verkleinert die JVM den Heap bis zum Minimum von -Xms.
Daher werden auf dem Server -Xms und -Xmx üblicherweise gleichgesetzt, um eine Größenänderung des Heaps nach jedem GC-Vorgang zu vermeiden. Der Heap-Speicher eines Objekts wird durch ein automatisches Speicherverwaltungssystem, den sogenannten Garbage Collector, freigegeben.
Der maximale Heap-Speicher ist die Obergrenze des von der JVM verwendeten Speichers; im tatsächlichen Betrieb wird nur so viel verbraucht wie benötigt. Standardmäßig wird maximal ein Drittel der gesamten Heap-Größe der Young Generation zugewiesen.
Um das ursprüngliche Problem zu lösen: Wenn jedes Programm mit der Standardkonfiguration gestartet wird und mehrere Anwendungen auf einem Server bereitgestellt werden, entsteht ein Speichermangel, der zu einer gewissen Verschwendung führt. Die einfachste Maßnahme besteht darin, beim Ausführen von java -jar die entsprechenden JVM-Speicherparameter hinzuzufügen.
java -Xms64m -Xmx128m -jar xxx.jar
Das Projekt verwendet Docker-Bereitstellung. Sehen wir uns zunächst die ursprüngliche Dockerfile an:
EXPOSE 8061
ADD target/certif-system-2.1.0.jar /certif-system-2.1.0.jar
ENTRYPOINT ["java", "-jar","/certif-system-2.1.0.jar"]
Spring-Boot-Optimierung
JVM-Speicher begrenzen
Spring Boot gibt alle Umgebungsvariablen an die Anwendung weiter – aber JAVA_OPTS richtet sich nicht an die Anwendung, sondern an die Java-Laufzeitumgebung selbst. Daher müssen wir die Variable $JAVA_OPTS verwenden, um exec java aufzurufen. Dies erfordert einige kleine Änderungen an der Dockerfile:
ENTRYPOINT exec java $JAVA_OPTS -jar app.jar
Den docker-run-Befehl ausführen
Dies bedeutet, dass die JAVA_OPTS-Parameterinformationen in der Umgebungsvariable zur Laufzeit durch -e zurückgesetzt werden.
docker run -e JAVA_OPTS='-Xmx1344M -Xms1344M -Xmn448M - XX:MaxMetaspaceSize =192M - XX:MetaspaceSize =192M'
Rolle von java.security.egd
SecureRandom wird in verschiedenen Java-Komponenten häufig verwendet und kann zuverlässig Zufallszahlen generieren. In Szenarien, in denen eine große Anzahl von Zufallszahlen erzeugt wird, sinkt jedoch die Leistung. In diesem Fall können Sie „-Djava.security.egd=file:/dev/./urandom“ verwenden, um den Zufallszahlengenerierungsprozess zu beschleunigen.
Es wird empfohlen, den Zufallszahlengenerator als /dev/./urandom anzugeben, wenn viele Zufallszahlen benötigt werden.
Bitte beachten Sie die vierte Zeile des folgenden Quellcodes für die Ursache des Fehlers. Wenn der Parameter java.security.egd file:/dev/random oder file:/dev/urandom angibt, wird der parameterlose NativeSeedGenerator-Konstruktor aufgerufen, und dieser verwendet standardmäßig file:/dev/random. Der Code von OpenJDK unterscheidet sich vom HotSpot-Code. OpenJDK verwendet diese Variable bei der späteren Zufallszahlengenerierung nicht.
Verwandte Artikel
-
6 optionale Technologien für die Datenspeicherung
Wissensdatenbank-Team
Weitere Sonderangebote entdecken
-
Kurznachrichtendienst (SMS) und E-Mail-Dienst
50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00
