2018/04/08 Nachtrag: Der Zusammenfassung wurde eine Beschreibung zu Java 10 hinzugefügt.
02.06.2017 Nachtrag: Der Inhalt der Umfrage wurde zusammengefasst und die Beschreibung erheblich aktualisiert.
Nachtrag 2017/06/01: "Bemühungen für dieses Problem nach JDK 8/9" am Ende des Artikels hinzugefügt. Es scheint, dass es in 8u121.b34 und 8u131.b06 von OpenJDK8 behoben wurde (ich werde separat prüfen, ob es wirklich behoben ist). Beide Problemumgehungen wurden seit 2017 veröffentlicht. Überprüfen Sie daher, ob Sie Ihr JDK / JRE nicht häufig aktualisieren.
Java10 unterstützt jetzt offiziell Docker-Container. Das Java-Programm im Docker-Container kann jetzt die im Docker-Container festgelegten Ressourceneinstellungen wie CPU und Speicher erfassen, sodass das in diesem Artikel beschriebene Menikoa-Problem behoben wird.
In Bezug auf die Docker-Unterstützung für Java10 wird der folgende Qiita-Artikel einschließlich der Funktionsprüfung ausführlich zusammengefasst.
https://qiita.com/c9katayama/items/b427a008623f0e228c50
Wenn Sie die Anzahl der CPUs mit Docker, Task-Set usw. in der Menicoa-Umgebung auf eine kleine Anzahl beschränken, kann die Leistung von Java-Programmen erheblich sinken. Es gibt zwei Möglichkeiten, damit umzugehen.
Wenn Sie nur die Option zum Angeben der CPU-Kern-ID verwenden möchten, z. B. --cpuset-cpus des Befehls --docker, können Sie dies mit einem JDK-Update behandeln. (Java 8 8u121.b34 oder höher oder 8u131.b06 oder höher) --Wenn Sie eine Option verwenden, die keine CPU-Kern-ID angibt, wie z. B. den Docker-Befehl --cpus, müssen Sie beim Starten des Java-Programms die entsprechenden JVM-Parametereinstellungen anpassen.
In einer Menicoa-Umgebung (einem Computer mit einer großen Anzahl von CPU-Kernen) sinkt die Leistung erheblich, wenn ein Java-Programm auf einem Docker ausgeführt wird, der Ressourcenbeschränkungen wie die Anzahl der CPU-Kerne aufweist. Gleiches gilt, wenn ein Java-Programm mit einer Containertechnologie oder einem Befehl (z. B. Tasket-Befehl) ausgeführt wird, der Ressourcenbeschränkungen durch andere Gruppen als Docker verwendet.
In Java werden die folgenden JVM-Parameter basierend auf den Hardwareressourcen des Computers festgelegt.
Das ist an sich in Ordnung. In Java werden die JVM-Parameter jedoch basierend auf den Hardwareressourcen des Hostcomputers und nicht auf den begrenzten Hardwareressourcen bestimmt, selbst wenn die Hardwareressourcen durch cgroup wie Docker begrenzt sind.
Wenn beispielsweise ein Java-Programm auf einem "Docker-Container mit 20 CPU-Kernen" auf einem Host-Computer mit 20 CPU-Kernen ausgeführt wird, ermittelt Java die JVM-Parameter basierend auf den 20 Kernen. .. Die Tatsache, dass nur ein CPU-Kern im Docker-Container verwendet werden kann, bleibt jedoch gleich, sodass Java-Programme zusätzlich zur Verarbeitung der Anwendung unter dem Overhead einer großen Anzahl von GC-Threads und JIT-Kompilierungsthreads leiden, was zu einer erheblichen Leistungsminderung führt. ..
Seien Sie vorsichtig mit denen, die die folgenden Bedingungen erfüllen.
Es gibt zwei Hauptansätze, um damit umzugehen.
Wie unter "(Referenz) Beheben dieses Problems nach JDK8 / 9" in diesem Artikel beschrieben, wurden Probleme in Bezug auf Gruppen in der neuen Version von JDK8 / 9 teilweise behoben. Im Fall von JDK8 wird dies durch ein Update auf die folgende Version oder höher behoben.
--8u121.b34 oder höher --8u131.b06 oder höher
Die Bestätigungsergebnisse im Docker-Container mit dem JDK vor und nach der Aktion sind unten aufgeführt. Ich habe es in einer Umgebung mit 8 logischen CPU-Kernen bestätigt, aber im JDK wird das Ressourcenlimit nach dem Umgang mit dem Docker-Befehl --cpuset-cpus im JVM-Parameter korrekt wiedergegeben.
#--------------------------------------------------------------
#In der Voraktion JDK--cpuset-Die Einstellung der Option cpus wird im JVM-Parameter nicht berücksichtigt
#--------------------------------------------------------------
$ docker run --rm --cpuset-cpus 0-1 openjdk:8u77-b03 java -XX:+PrintFlagsFinal -version | egrep "CICompilerCount |ParallelGCThreads"
intx CICompilerCount := 4 {product}
uintx ParallelGCThreads = 8 {product}
openjdk version "1.8.0_03-Ubuntu"
OpenJDK Runtime Environment (build 1.8.0_03-Ubuntu-8u77-b03-3ubuntu3-b03)
OpenJDK 64-Bit Server VM (build 25.03-b03, mixed mode)
#--------------------------------------------------------------
#Nach dem Umgang mit JDK--cpuset-Die Einstellung der Option cpus spiegelt sich im JVM-Parameter wider
#--------------------------------------------------------------
$ docker run --rm --cpuset-cpus 0-1 openjdk:8u131-b11 java -XX:+PrintFlagsFinal -version | egrep "CICompilerCount |ParallelGCThreads"
intx CICompilerCount := 2 {product}
uintx ParallelGCThreads = 2 {product}
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-0ubuntu1.16.04.2-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)
Im Fall einer Option, die keine bestimmte CPU-Kern-ID wie --cpus des Docker-Befehls angibt, scheint das Problem jedoch auch mit Java nach der Behandlung nicht zu vermeiden. Ich habe es nicht ausprobiert, aber es ist wahrscheinlich, dass dieses Problem auch für die Optionen --cpu-quota und --cpu-period nicht vermieden werden kann. In diesem Fall müssen die JVM-Parametereinstellungen angepasst werden. Dies ist eine weitere Problemumgehung.
#--------------------------------------------------------------
#Nach dem Umgang damit auch in JDK--Keine Auswirkung mit den Einstellungen der CPU-Option
#--------------------------------------------------------------
docker run --rm --cpus 2.0 openjdk:8u131-b11 java -XX:+PrintFlagsFinal -version | egrep "CICompilerCount |ParallelGCThreads"
intx CICompilerCount := 4 {product}
uintx ParallelGCThreads = 8 {product}
openjdk version "1.8.0_131"
OpenJDK Runtime Environment (build 1.8.0_131-8u131-b11-0ubuntu1.16.04.2-b11)
OpenJDK 64-Bit Server VM (build 25.131-b11, mixed mode)
Dies kann durch Optimieren gelöst werden, indem der JVM-Parameter beim Ausführen des Java-Programms auf dem Docker-Container festgelegt wird. Es ist zeitaufwändiger als ein JDK-Update, aber das zuverlässigste.
Möglicherweise möchten Sie den GC auf einen seriellen GC einstellen, wenn die folgenden Bedingungen erfüllt sind:
#Ändern Sie den GC in den seriellen GC
java -XX:+UseSerialGC ...
Es wird jedoch gesagt, dass die serielle GC in Zukunft abgeschafft wird. Seien Sie also vorsichtig, wenn Sie Java aktualisieren möchten.
Wenn Sie die parallele GC verwenden möchten, die die Standard-GC von Java8 ist, können Sie damit umgehen, indem Sie die Anzahl der Threads der parallelen GC verringern. Die entsprechende Anzahl von Threads hängt von der im Docker-Befehl angegebenen CPU-Ressourcenbeschränkung ab. Daher ist eine Optimierung erforderlich.
#Setzen Sie die Anzahl der parallelGC-Threads auf 1
java XX:ParallelGCThreads=1 ...
Ich habe CSM, G1 usw. nicht festgelegt, daher werde ich es nicht beschreiben, aber im Grunde können Sie die Anzahl der Threads entsprechend den CPU-Ressourcen festlegen.
Die JIT-Kompilierung wird auch auf mehreren Threads ausgeführt, und in der Menikoa-Umgebung wird eine bestimmte Anzahl von Threads generiert. Daher ist auch eine Optimierung gemäß dem CPU-Ressourcenlimit erforderlich.
#Setzen Sie die Anzahl der JIT-Kompilierungsthreads auf 1
java -XX:CICompilerCount=1 ...
Die JVM-Parameter werden basierend auf dem Java-Programm auf der virtuellen Maschine und den Hardwareressourcen der virtuellen Maschine bestimmt. Selbst wenn sich die Hostmaschine in einer Menicoa-Umgebung befindet, werden die JVM-Parameter daher basierend auf den der virtuellen Maschine zugewiesenen Hardwareressourcen festgelegt.
Dieses Problem wurde seit JDK9 erkannt, und es scheint, dass das Problem auf JDK8 zurückportiert wurde, sofern der Supportstatus des Problems angezeigt wird. Und es wurde in JDK8 "8u121.b34" und "8u131.b06" behandelt.
Recommended Posts