[DOCKER] Leistungseinbußen bei Java-Containern in der Menicoa-Umgebung

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.

Zusammenfassung (Java 10-Migration)

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

Zusammenfassung (Java 8/9)

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.

Ereignis auftreten

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.

Ursache

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.

Problemumgehung

Es gibt zwei Hauptansätze, um damit umzugehen.

Problemumgehung 1: JDK-Update

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)

Problemumgehung 2: JVM-Parametereinstellungen

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.

Ändern Sie den GC in den seriellen GC

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.

Ändern Sie die Anzahl der GC-Threads

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.

Reduzieren Sie die Anzahl der JIT-Kompilierungsthreads.

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

(Referenz) Für virtuelle Maschinen

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.

(Referenz) Versionsinformationen, wenn ein Problem auftritt

(Referenz) Bemühungen um dieses Problem nach JDK8 / 9

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.

  1. JDK-8140793 - getAvailableProcessors may incorrectly report the number of cpus in Docker container
  1. JDK-8172318 - [linux] Experimental support for cgroup memory limits in container (ie Docker) environments

Recommended Posts

Leistungseinbußen bei Java-Containern in der Menicoa-Umgebung
JavaFX-Umgebungskonstruktion in Java 13
Java Spring-Umgebung in vs Code
Spielen Sie die Framework 2.6 (Java) -Umgebungskonstruktion mit Eclipse
In einer VM-Umgebung erstellte Java-Anwendungsentwicklungsumgebung
Partisierung in Java
[Java] Umgebungskonstruktion
Wenn Java-Tests Umgebungsvariablen enthalten
Änderungen in Java 11
Lösung für NetBeans 8.2 funktioniert nicht in Java 9-Umgebung
Janken in Java
Java-Entwicklungsumgebung
Umfangsrate in Java
Punkte, die beim Erstellen der VS Code- und Java-Entwicklungsumgebung hängen bleiben
[Anfänger] Installieren Sie das Java-Entwicklungstool in der Cloud9-Entwicklungsumgebung.
FizzBuzz in Java
Ersetzen von Systemumgebungsvariablen durch Reflektion in Java
[LeJOS] Programmieren wir mindstorm-EV3 mit Java [Umgebungskonstruktion Teil 2]
So erstellen Sie eine Java-Umgebung in nur 3 Sekunden
Maven schlägt in der Java7-Umgebung fehl (schwerwiegende Warnung erhalten: protocol_version)
[Java] Holen Sie sich die Datei unabhängig von der Umgebung in das JAR
Lesen Sie JSON in Java
Interpreter-Implementierung durch Java
Machen Sie einen Blackjack mit Java
Janken App in Java
Einschränkungsprogrammierung in Java
Setzen Sie Java8 in Centos7
NVL-artiger Typ in Java
Verbinden Sie Arrays in Java
"Hallo Welt" in Java
Memo zur Java-Entwicklungsumgebung
Aufrufbare Schnittstelle in Java
[Zusammenfassung] Zum Beispiel die Vorbereitung der Java-Umgebung
Kommentare in der Java-Quelle
Azure funktioniert in Java
Formatieren Sie XML in Java
Leistungsoptimierung für Java-Apps
Einfache HTML-Spezialchars in Java
Boyer-Moore-Implementierung in Java
Hallo Welt in Java
Verwenden Sie OpenCV mit Java
WebApi-Memorandum mit Java
Typbestimmung in Java
Befehle in Java ausführen (Ping)
Verschiedene Threads in Java
Implementierung der Heap-Sortierung (in Java)
Zabbix API in Java
ASCII-Kunst in Java
Listen in Java vergleichen
POST JSON in Java
Java Entwicklungsumgebung Konstruktion
Fehler in Java ausdrücken
Erstellen Sie JSON in Java
Datumsmanipulation in Java 8
Was ist neu in Java 8?
Verwenden Sie PreparedStatement in Java
Java Performance Kapitel 3 Java Performance Toolbox
Was ist neu in Java 9,10,11
Parallele Ausführung in Java