So erwärmen Sie die JVM

1. Überblick

Die JVM ist eine der ältesten und dennoch leistungsfähigsten virtuellen Maschinen, die jemals gebaut wurden.

In diesem Artikel wird kurz beschrieben, was es bedeutet, eine JVM aufzuwärmen und wie dies zu tun ist.

2. Grundlagen der JVM-Architektur

Wenn ein neuer JVM-Prozess gestartet wird, werden alle erforderlichen Klassen von einer Instanz des ClassLoader in den Speicher geladen.

Dieser Vorgang läuft in drei Schritten ab:

  1. Bootstrap Class Loading: Der " Bootstrap Class Loader " lädt Java

Code und wichtige Java-Klassen wie java.lang.Object in den Speicher.

Diese geladenen Klassen befinden sich in JRE \ lib \ rt.jar .

  1. Erweiterungsklasse wird geladen : Die

ExtClassLoader ist für das Laden aller JAR-Dateien verantwortlich, die sich im Pfad java.ext.dirs befinden. In Nicht-Maven- oder Nicht-Gradle-basierten Anwendungen, in denen ein Entwickler JARs manuell hinzufügt, werden alle diese Klassen in dieser Phase geladen.

  1. Application Class Loading : Die

AppClassLoader lädt alle im Pfad der Anwendungsklasse befindlichen Klassen.

Dieser Initialisierungsprozess basiert auf einem Lazy-Load-Schema.

3. Was wärmt die JVM auf?

Sobald das Laden der Klassen abgeschlossen ist, werden alle wichtigen Klassen (die zum Zeitpunkt des Prozessstarts verwendet werden) in die https://www.ibm.com/support/knowledgecenter/de/SSAW57 8.5.5/com.ibm.websphere.nd verschoben .doc/ae/rdyn tunediskcache.html[JVM-Cache (nativer Code)]- Damit sind sie während der Laufzeit schneller verfügbar. Andere Klassen werden pro Anforderung geladen.

Die erste Anforderung an eine Java-Webanwendung ist oft wesentlich langsamer als die durchschnittliche Antwortzeit während der Lebensdauer des Prozesses. Diese Aufwärmphase kann normalerweise auf das langsame Laden von Klassen und die Just-in-Time-Kompilierung zurückgeführt werden.

Vor diesem Hintergrund müssen wir bei Anwendungen mit geringer Latenz alle Klassen vorher zwischenspeichern, damit sie sofort verfügbar sind, wenn sie zur Laufzeit aufgerufen werden.

  • Dieser Vorgang des Abstimmens der JVM wird als Aufwärmen bezeichnet. **

4. Abgestufte Zusammenstellung

Dank der Klangarchitektur der JVM werden häufig verwendete Methoden während des Anwendungslebenszyklus in den nativen Cache geladen.

Wir können diese Eigenschaft verwenden, um beim Start einer Anwendung kritische Methoden in den Cache zu laden. Insofern müssen wir ein VM-Argument mit dem Namen Tiered Compilation setzen:

-XX:CompileThreshold -XX:TieredCompilation

Normalerweise verwendet die VM den Interpreter, um Profilinformationen zu Methoden zu sammeln, die in den Compiler eingegeben werden. Im abgestuften Schema wird der Client-Compiler zusätzlich zum Interpreter verwendet, um kompilierte Versionen von Methoden zu generieren, die Profilinformationen über sich selbst sammeln.

Da kompilierter Code wesentlich schneller ist als interpretierter Code, wird das Programm während der Profilierungsphase mit einer besseren Leistung ausgeführt.

Anwendungen, die auf JBoss und JDK Version 7 mit aktiviertem VM-Argument ausgeführt werden, neigen dazu, nach einiger Zeit aufgrund eines dokumentierten bug abstürzen. Das Problem wurde in JDK Version 8 behoben.

Ein weiterer zu beachtender Punkt ist, dass wir zum Erzwingen des Ladens sicherstellen müssen, dass auf alle (oder die meisten) Klassen, die ausgeführt werden sollen, zugegriffen werden muss. Ähnlich wie beim Ermitteln der Codeabdeckung während des Komponententests.

Je mehr Code abgedeckt wird, desto besser ist die Leistung.

Im nächsten Abschnitt wird gezeigt, wie dies implementiert werden kann.

5. Manuelle Implementierung

Wir können eine alternative Technik implementieren, um die JVM aufzuwärmen. In diesem Fall kann ein einfaches manuelles Aufwärmen das tausendfache Wiederholen der Erstellung verschiedener Klassen umfassen, sobald die Anwendung gestartet wird.

Zuerst müssen wir eine Dummy-Klasse mit einer normalen Methode erstellen:

public class Dummy {
    public void m() {
    }
}

Als Nächstes müssen Sie eine Klasse mit einer statischen Methode erstellen, die beim Start der Anwendung mindestens 100000 Mal ausgeführt wird. Bei jeder Ausführung wird eine neue Instanz der zuvor genannten Dummy-Klasse erstellt, die wir zuvor erstellt haben:

public class ManualClassLoader {
    protected static void load() {
        for (int i = 0; i < 100000; i++) {
            Dummy dummy = new Dummy();
            dummy.m();
        }
    }
}

Um den Performance-Gewinn ** messen zu können, müssen wir nun eine Hauptklasse erstellen. Diese Klasse enthält einen statischen Block, der einen direkten Aufruf der load () - Methode von ManualClassLoader enthält.

Innerhalb der Hauptfunktion rufen wir erneut die Methode load () von ManualClassLoader auf und erfassen die Systemzeit in Nanosekunden unmittelbar vor und nach unserem Funktionsaufruf. Zum Schluss ziehen wir diese Zeiten ab, um die tatsächliche Ausführungszeit zu ermitteln.

Wir müssen die Anwendung zweimal ausführen. einmal mit dem load () Methodenaufruf innerhalb des statischen Blocks und einmal ohne diesen Methodenaufruf:

public class MainApplication {
    static {
        long start = System.nanoTime();
        ManualClassLoader.load();
        long end = System.nanoTime();
        System.out.println("Warm Up time : " + (end - start));
    }
    public static void main(String[]args) {
        long start = System.nanoTime();
        ManualClassLoader.load();
        long end = System.nanoTime();
        System.out.println("Total time taken : " + (end - start));
    }
}

Nachfolgend werden die Ergebnisse in Nanosekunden wiedergegeben:

| =================================================== Mit Warm Up | Kein Aufwärmen | Unterschied (%) | | | 730 | | | 1256 | | | 905 | | | 706 | | | 1053 | ==============================================

Wie erwartet, zeigt der Ansatz beim Aufwärmen eine viel bessere Leistung als die normale.

Natürlich ist dies ein sehr einfacher Benchmark und bietet nur einen Einblick in die Auswirkungen dieser Technik auf Oberflächenebene. Es ist auch wichtig zu verstehen, dass wir uns bei einer realen Anwendung mit den typischen Codepfaden im System aufwärmen müssen.

6. Werkzeuge

Wir können auch mehrere Tools verwenden, um die JVM aufzuwärmen. Eines der bekanntesten Tools ist das Java Microbenchmark Harness, JMH . Es wird im Allgemeinen für das Micro-Benchmarking verwendet. Sobald es geladen ist, schlägt es wiederholt ein Code-Snippet ab und überwacht den Aufwärm-Iterationszyklus.

Um es zu benutzen, müssen wir eine weitere Abhängigkeit zu pom.xml hinzufügen:

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.19</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.19</version>
</dependency>

Wir können die neueste Version von JMH in Central Maven Repository überprüfen.

Alternativ können Sie das Maven-Plugin von JMH verwenden, um ein Beispielprojekt zu generieren:

mvn archetype:generate \
    -DinteractiveMode=false \
    -DarchetypeGroupId=org.openjdk.jmh \
    -DarchetypeArtifactId=jmh-java-benchmark-archetype \
    -DgroupId=com.baeldung \
    -DartifactId=test \
    -Dversion=1.0

Als Nächstes erstellen wir eine main -Methode:

public static void main(String[]args)
  throws RunnerException, IOException {
    Main.main(args);
}

Nun müssen wir eine Methode erstellen und mit der Annotation @ Benchmark von JMH kommentieren:

@Benchmark
public void init() {
   //code snippet
}

Innerhalb dieser init -Methode müssen wir Code schreiben, der zum Aufwärmen wiederholt ausgeführt werden muss.

** 7. Performance-Benchmark

**

In den letzten 20 Jahren bezogen sich die meisten Beiträge zu Java auf den GC (Garbage Collector) und JIT (Just In Time Compiler). Fast alle online gefundenen Leistungsbenchmarks werden auf einer JVM durchgeführt, die bereits seit einiger Zeit läuft. Jedoch,

Http://www.eecg.toronto.edu/~yuan/papers/osdi16-hottub.pdf[ Beihang University ]hat jedoch einen Benchmark-Bericht veröffentlicht, der die Aufwärmzeit von JVM berücksichtigt. Sie verwendeten Hadoop- und Spark-basierte Systeme, um massive Daten zu verarbeiten:

HotTub bezeichnet hier die Umgebung, in der die JVM aufgewärmt wurde.

Wie Sie sehen, kann die Beschleunigung insbesondere für relativ kleine Leseoperationen erheblich sein. Daher sind diese Daten interessant.

8. Fazit

In diesem kurzen Artikel haben wir gezeigt, wie die JVM Klassen lädt, wenn eine Anwendung gestartet wird, und wie wir die JVM aufwärmen können, um eine Leistungssteigerung zu erzielen.

Dieses Buch enthält weitere Informationen und Richtlinien zum Thema, wenn Sie fortfahren möchten.

Und wie immer ist der vollständige Quellcode verfügbar: over auf GitHub .