Heim > Java > javaLernprogramm > Hauptteil

Testen der Auswirkung von Garbage Collector GC auf den Durchsatz in Java

高洛峰
Freigeben: 2017-01-17 15:48:26
Original
1719 Leute haben es durchsucht

Als ich mir das Glossar der Speicherverwaltungsbegriffe ansah, entdeckte ich versehentlich die Definition von „Schwein in der Python (Hinweis: Es ist ein bisschen wie die gierige und unzureichende Schlange, die den Elefanten verschlingt)“ auf Chinesisch, also bin ich darauf gestoßen mit diesem Artikel. 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 Hoffnungsschimmer, 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, die wir verwenden, um die Auswirkung von GC auf den Durchsatz zu demonstrieren, ist nur 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 jeder Aufnahme 100 ms lang in den Ruhezustand versetzt.
PigDigester – 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.

package eu.plumbr.demo;
public class PigInThePython {
  static volatile List pigs = new ArrayList();
  static volatile int pigsEaten = 0;
  static final int ENOUGH_PIGS = 5000;
  public static void main(String[] args) throws InterruptedException {
    new PigEater().start();
    new PigDigester().start();
  }
  static class PigEater extends Thread {
    @Override
    public void run() {
      while (true) {
        pigs.add(new byte[32 * 1024 * 1024]); //32MB per pig
        if (pigsEaten > ENOUGH_PIGS) return;
        takeANap(100);
      }
    }
  }
  static class PigDigester extends Thread {
    @Override
    public void run() {
      long start = System.currentTimeMillis();
      while (true) {
        takeANap(2000);
        pigsEaten+=pigs.size();
        pigs = new ArrayList();
        if (pigsEaten > ENOUGH_PIGS)  {
          System.out.format("Digested %d pigs in %d ms.%n",pigsEaten, System.currentTimeMillis()-start);
          return;
        }
      }
    }
  }
  static void takeANap(int ms) {
    try {
      Thread.sleep(ms);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
Nach dem Login kopieren

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

GC-Konfigurationsbeispiel

Werfen wir einen Blick auf die Leistung bei der Verwendung zweier verschiedener Konfigurationssysteme. Unabhängig von der Konfiguration läuft die Anwendung auf einem Dual-Core-Mac (OS X10.9.3) mit 8 GB RAM.

Die 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 Generation 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 die bessere Leistung erbringt (d. h. wie viele Schweine pro Sekunde gefressen werden können). Erinnern)? Wer seine Chips auf die Erstkonfiguration setzt, wird enttäuscht sein. Die Ergebnisse sind 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ßer neuer Generation, Parellel GC) kann 9,2 Schweine pro Sekunde fressen

Nun schauen wir uns dieses Ergebnis 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.

GC-Ergebnisse analysieren

Der Grund ist eigentlich nicht kompliziert. Sie müssen sich nur genau ansehen, was der GC tut, wenn Sie den Test durchführen, um die Antwort zu finden. Hier wählen Sie das Tool aus, das Sie verwenden möchten. Mit Hilfe von jstat habe ich das Geheimnis dahinter entdeckt. Der Befehl lautet wahrscheinlich wie folgt:

jstat -gc -t -h20 PID 1s
Nach dem Login kopieren

Durch die Analyse der Daten habe ich festgestellt, dass Konfiguration 1 1129 GC-Zyklen (YGCT_FGCT) durchlaufen hat und insgesamt ausgegeben hat von 63,723 Sekunden:

Timestamp        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
Nach dem Login kopieren

Die zweite Konfiguration pausierte insgesamt 168 Mal (YGCT+FGCT) und dauerte nur 11,409 Sekunden.

Timestamp        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
Nach dem Login kopieren

Wenn man bedenkt, dass die Arbeitsbelastung in beiden Fällen gleich ist, kann der GC in diesem Schweinefress-Experiment, wenn er keine langlebigen Objekte findet, das Müllobjekt 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 hat zwei Zwecke. Das Erste und Wichtigste ist, dass ich diese zuckende Pythonschlange aus meinem Kopf verbannen möchte. 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.

Weitere Artikel zum Testen der Auswirkungen von Garbage Collector GC auf den Durchsatz in Java finden Sie auf der chinesischen PHP-Website!

Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage