Ich habe zufällig die Definition von „Schwein im Python (Hinweis: Es ist ein bisschen wie die gierige und unzureichende Schlange, die den Elefanten verschlingt)“ auf Chinesisch entdeckt, als ich mir das Glossar zur Speicherverwaltung angesehen habe, und habe mir diesen Artikel ausgedacht. Oberflächlich betrachtet bezieht sich dieser Begriff darauf, dass der GC ständig große Objekte von einer Generation zur nächsten fördert. Dies ist so, als würde eine Python ihre Beute im Ganzen verschlucken, so dass sie sich während der Verdauung nicht bewegen kann.
In den nächsten 24 Stunden war mein Geist voller Bilder dieser erstickenden Python, die ich nicht loswerden konnte. Wie Psychiater sagen, besteht der beste Weg, Angst zu lindern, darin, sie auszusprechen. Daher dieser Artikel. Aber die nächste Geschichte, über die wir sprechen wollen, ist nicht Python, sondern GC-Tuning. Ich schwöre bei Gott.
Jeder weiß, dass GC-Pausen leicht zu Leistungsengpässen führen können. Moderne JVMs werden bei ihrer Veröffentlichung mit fortschrittlichen Garbage Collectors ausgeliefert, aber meiner Erfahrung nach ist es äußerst schwierig, die optimale Konfiguration für eine bestimmte Anwendung zu finden. Bei der manuellen Abstimmung gibt es vielleicht noch einen Funken Hoffnung, aber Sie müssen die genaue Mechanik des GC-Algorithmus verstehen. In diesem Zusammenhang wird Ihnen dieser Artikel hilfreich sein. Im Folgenden erläutere ich anhand eines Beispiels, wie sich eine kleine Änderung in der JVM-Konfiguration auf den Durchsatz Ihrer Anwendung auswirkt.
Beispiel
Die Anwendung, mit der wir den Einfluss von GC auf den Durchsatz demonstrierten, war ein einfaches Programm. Es enthält zwei Threads:
PigEater Es wird den Vorgang nachahmen, bei dem eine Riesenpython ein großes, fettes Schwein frisst. Der Code tut dies, indem er 32 MB Bytes zu java.util.List hinzufügt und nach jedem Schlucken 100 ms lang schläft.
PigDgester Es simuliert den Prozess der asynchronen Verdauung. Der Code, der die Verdauung implementiert, setzt einfach die Liste der Schweine auf leer. Da dies ein ermüdender Prozess ist, schläft dieser Thread jedes Mal für 2000 ms, nachdem die Referenz gelöscht wurde.
Beide Threads laufen in einer While-Schleife und fressen und verdauen, bis die Schlange voll ist. Dafür müssten etwa 5.000 Schweine verzehrt werden.
Kopieren Sie den Codecode wie folgt:
Paket eu.plumbr.demo;
öffentliche Klasse PigInThePython {
static volatile List pigs = new ArrayList();
static volatile int pigsEaten = 0;
static final int ENOUGH_PIGS = 5000;
public static void main(String[] args) löst InterruptedException {
new PigEater().start();
new PigDigester().start();
}
statische Klasse PigEater erweitert Thread {
@Override
public void run() {
while (wahr) {
pigs.add(new byte[32 * 1024 * 1024]); //32 MB pro Schwein
if (pigsEaten > ENOUGH_PIGS) return;
takeANap(100);
}
}
}
statische Klasse PigDigester erweitert Thread {
@Override
public void run() {
langer Start = System.currentTimeMillis();
while (wahr) {
takeANap(2000);
pigsEaten+=pigs.size();
Schweine = neue ArrayList();
if (pigsEaten > ENOUGH_PIGS) {
System.out.format("Verdaute %d Schweine in %d ms.%n",pigsEaten, System.currentTimeMillis()-start);
zurückkehren;
}
}
}
}
static void takeANap(int ms) {
versuchen {
Thread.sleep(ms);
} Catch (Ausnahme e) {
e.printStackTrace();
}
}
}
Nun definieren wir den Durchsatz dieses Systems als „die Anzahl der Schweine, die pro Sekunde verdaut werden können“. Wenn man bedenkt, dass alle 100 ms ein Schwein in diese Python gestopft wird, können wir sehen, dass der theoretische maximale Durchsatz dieses Systems 10 Schweine/Sekunde erreichen kann.
Beispiel für eine GC-Konfiguration
Werfen wir einen Blick auf die Leistung der Verwendung zweier unterschiedlicher Konfigurationssysteme. Unabhängig von der Konfiguration läuft die Anwendung auf einem Dual-Core-Mac (OS X10.9.3) mit 8 GB RAM.
Erste Konfiguration:
1,4G Heap (-Xms4g -Xmx4g)
2. Verwenden Sie CMS, um die alte Generation zu bereinigen (-XX:+UseConcMarkSweepGC) und verwenden Sie den Parallelkollektor, um die neue Generation zu bereinigen (-XX:+UseParNewGC).
3. Weisen Sie der neuen Generation 12,5 % des Heaps (-Xmn512m) zu und begrenzen Sie die Größe des Eden-Bereichs und des Survivor-Bereichs auf die gleiche Größe.
Die zweite Konfiguration ist etwas anders:
1,2G Heap (-Xms2g -Xms2g)
2. Sowohl die neue als auch die alte Generation verwenden Parellel GC (-XX:+UseParallelGC).
3. Weisen Sie der neuen Generation 75 % des Heaps zu (-Xmn 1536 m).
4. Jetzt ist es an der Zeit, eine Wette abzuschließen: Welche Konfiguration wird die bessere Leistung erbringen (wie viele Schweine können pro Sekunde gefressen werden, erinnern Sie sich)? Wer seine Chips auf die Erstkonfiguration setzt, wird enttäuscht sein. Das Ergebnis ist genau das Gegenteil:
1. Die erste Konfiguration (großer Heap, große alte Generation, CMS GC) kann 8,2 Schweine pro Sekunde fressen
2. Die zweite Konfiguration (kleiner Heap, große neue Generation, Parellel GC) kann 9,2 Schweine pro Sekunde fressen
Schauen wir uns dieses Ergebnis nun objektiv an. Die zugewiesenen Ressourcen sind doppelt so hoch, aber der Durchsatz wird um 12 % erhöht. Dies widerspricht dem gesunden Menschenverstand, daher ist es notwendig, die Vorgänge weiter zu analysieren.
Analysieren Sie die GC-Ergebnisse
Der Grund ist eigentlich nicht kompliziert. Sie können die Antwort finden, indem Sie sich genauer ansehen, was der GC beim Ausführen des Tests tut. Hier wählen Sie das Tool aus, das Sie verwenden möchten. Mit Hilfe von jstat habe ich das Geheimnis dahinter entdeckt. Der Befehl sieht wahrscheinlich so aus:
Kopieren Sie den Codecode wie folgt:
jstat -gc -t -h20 PID 1s
Bei der Analyse der Daten fiel mir auf, dass Konfiguration 1 1129 GC-Zyklen (YGCT_FGCT) durchlief, was insgesamt 63,723 Sekunden dauerte:
Kopieren Sie den Codecode wie folgt:
Zeitstempel S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
594,0 174720,0 174720,0 163844,1 0,0 174848,0 131074,1 3670016,0 2621693,5 21248,0 2580,9 1006 63,182 116 0,236 63.419
595,0 174720,0 174720,0 163842,1 0,0 174848,0 65538,0 3670016,0 3047677,9 21248,0 2580,9 1008 63,310 117 0,236 63.546
596,1 174720,0 174720,0 98308,0 163842,1 174848,0 163844,2 3670016,0 491772,9 21248,0 2580,9 1010 63,354 118 0,240 63.595
597,0 174720,0 174720,0 0,0 163840,1 174848,0 131074,1 3670016,0 688380,1 21248,0 2580,9 1011 63,482 118 0,240 63.723
Die zweite Konfiguration pausierte insgesamt 168 Mal (YGCT+FGCT) und dauerte nur 11,409 Sekunden.
Kopieren Sie den Codecode wie folgt:
Zeitstempel S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
539,3 164352,0 164352,0 0,0 0,0 1211904,0 98306,0 524288,0 164352,2 21504,0 2579,2 27 2,969 141 8,441 11,409
540,3 164352,0 164352,0 0,0 0,0 1211904,0 425986,2 524288,0 164352,2 21504,0 2579,2 27 2,969 141 8,441 11,409
541,4 164352,0 164352,0 0,0 0,0 1211904,0 720900,4 524288,0 164352,2 21504,0 2579,2 27 2,969 141 8,441 11,409
542,3 164352,0 164352,0 0,0 0,0 1211904,0 1015812,6 524288,0 164352,2 21504,0 2579,2 27 2,969 141 8,441 11,409
Wenn man bedenkt, dass die Arbeitsbelastung in beiden Fällen gleich ist, kann der GC in diesem Schweinefressexperiment, wenn er keine langlebigen Objekte findet, Müllobjekte schneller bereinigen. Bei der ersten Konfiguration beträgt die Häufigkeit des GC-Betriebs etwa das 6- bis 7-fache und die Gesamtpausenzeit das 5- bis 6-fache.
Das Erzählen dieser Geschichte dient zwei Zwecken. Zuallererst und am wichtigsten war, dass ich diese zuckende Pythonschlange aus meinem Kopf verbannen wollte. Ein weiterer offensichtlicherer Vorteil besteht darin, dass das GC-Tuning eine sehr geschickte Erfahrung ist und ein gründliches Verständnis der zugrunde liegenden Konzepte erfordert. Obwohl es sich bei der in diesem Artikel verwendeten Anwendung nur um eine sehr häufige Anwendung handelt, haben die unterschiedlichen Ergebnisse der Auswahl auch große Auswirkungen auf Ihre Durchsatz- und Kapazitätsplanung. In realen Anwendungen wird der Unterschied hier noch größer sein. Es liegt also an Ihnen, ob Sie diese Konzepte beherrschen oder sich einfach auf Ihre tägliche Arbeit konzentrieren und Plumbr die für Ihre Anforderungen am besten geeignete GC-Konfiguration ermitteln lassen.