Comment réchauffer la machine virtuelle

1. Vue d’ensemble

La JVM est l’une des machines virtuelles les plus anciennes et les plus puissantes jamais construites.

Dans cet article, nous examinons rapidement ce que signifie réchauffer une machine virtuelle Java et comment le faire.

2. Notions de base sur l’architecture JVM

À chaque démarrage d’un nouveau processus JVM, toutes les classes requises sont chargées en mémoire par une instance de ClassLoader .

Ce processus se déroule en trois étapes:

  1. Chargement de classes d’amorçage: Le “ Bootstrap Class Loader ” charge Java

code et les classes Java essentielles telles que java.lang.Object en mémoire.

Ces classes chargées résident dans JRE \ lib \ rt.jar .

  1. Chargement de classe d’extension : Le

ExtClassLoader est responsable du chargement de tous les fichiers JAR situés dans le chemin java.ext.dirs . Dans les applications non Maven ou Gradle, dans lesquelles un développeur ajoute des fichiers JAR manuellement, toutes ces classes sont chargées au cours de cette phase.

  1. Chargement en cours d’application : Le

AppClassLoader charge toutes les classes situées dans le chemin de classes de l’application.

Ce processus d’initialisation est basé sur un schéma de chargement paresseux.

3. Qu’est-ce qui réchauffe la JVM

Une fois le chargement de classe terminé, toutes les classes importantes (utilisées au moment du démarrage du processus) sont insérées dans https://www.ibm.com/support/knowledgecenter/en/SSAW57 8.5.5/com.ibm.websphere.nd .doc/ae/rdyn tunediskcache.html[Cache JVM (code natif)]- ce qui les rend accessibles plus rapidement pendant l’exécution. Les autres classes sont chargées à la demande.

La première requête adressée à une application Web Java est souvent beaucoup plus lente que le temps de réponse moyen pendant la durée de vie du processus. Cette période d’échauffement peut généralement être attribuée à un chargement de classe paresseux et à une compilation juste à temps.

Gardez cela à l’esprit, pour les applications à faible temps de latence, nous devons au préalable mettre en cache toutes les classes afin qu’elles soient disponibles instantanément lorsqu’elles sont utilisées au moment de l’exécution.

  • Ce processus de réglage de la machine virtuelle Java est connu sous le nom de préchauffage. **

4. Compilation étagée

Grâce à l’architecture sonore de la machine virtuelle Java, les méthodes fréquemment utilisées sont chargées dans le cache natif au cours du cycle de vie de l’application.

Nous pouvons utiliser cette propriété pour forcer le chargement de méthodes critiques dans le cache au démarrage d’une application. Dans cette mesure, nous devons définir un argument de machine virtuelle nommé Tiered Compilation :

-XX:CompileThreshold -XX:TieredCompilation

Normalement, la machine virtuelle utilise l’interpréteur pour collecter des informations de profilage sur les méthodes introduites dans le compilateur. Dans le schéma à plusieurs niveaux, en plus de l’interpréteur, le compilateur client est utilisé pour générer des versions compilées de méthodes qui collectent des informations de profilage les concernant.

Le code compilé étant nettement plus rapide que le code interprété, le programme s’exécute avec de meilleures performances pendant la phase de profilage.

Les applications exécutées sur JBoss et JDK version 7 avec cet argument de machine virtuelle activé ont tendance à se bloquer après un certain temps en raison de la documentation https://issues.jboss.org/browse/JBEAP-26 ]. Le problème a été corrigé dans JDK version 8.

Un autre point à noter ici est que pour forcer le chargement, nous devons nous assurer que toutes les classes (ou la plupart) à exécuter doivent être accessibles. C’est similaire à la détermination de la couverture de code lors des tests unitaires.

Plus le code est couvert, meilleure sera la performance.

La section suivante montre comment cela peut être implémenté.

5. Mise en œuvre manuelle

Nous pouvons mettre en œuvre une autre technique pour réchauffer la machine virtuelle. Dans ce cas, un simple préchauffage manuel peut inclure la répétition de la création de classes différentes des milliers de fois dès le démarrage de l’application.

Tout d’abord, nous devons créer une classe fictive avec une méthode normale:

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

Ensuite, nous devons créer une classe qui a une méthode statique qui sera exécutée au moins 100 000 fois dès que l’application démarrera et à chaque exécution, elle créera une nouvelle instance de la classe factice susmentionnée précédemment créée:

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

Maintenant, afin de mesurer le gain de performance , nous devons créer une classe principale. Cette classe contient un bloc statique qui contient un appel direct à la méthode load () de ManualClassLoader.

Dans la fonction principale, nous appelons à nouveau la méthode load () de ManualClassLoader et capturons l’heure du système en nanosecondes juste avant et après notre appel de fonction. Enfin, nous soustrayons ces heures pour obtenir l’heure d’exécution réelle.

Nous devons exécuter l’application deux fois; une fois avec l’appel de méthode load () dans le bloc statique et une fois sans cet appel de méthode:

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));
    }
}

Ci-dessous, les résultats sont reproduits en nanosecondes:

| =========================================== Up | Pas d’échauffement | Différence (%) | | | 730 | | | 1256 | | | 905 | | | 706 | | | 1053 | ==========================================

Comme on pouvait s’y attendre, l’approche d’échauffement montre une performance bien meilleure que celle normale.

Bien entendu, il s’agit d’un repère très simpliste qui ne donne qu’un aperçu de l’impact de cette technique au niveau de la surface. En outre, il est important de comprendre que, avec une application du monde réel, nous devons nous familiariser avec les chemins de code typiques du système.

6. Outils

Nous pouvons également utiliser plusieurs outils pour réchauffer la machine virtuelle. Un des outils les plus connus est le Java Microbenchmark Harness, JMH . Il est généralement utilisé pour le micro-benchmarking. Une fois chargé, il frappe plusieurs fois un extrait de code et surveille le cycle d’itération de préchauffage.

Pour l’utiliser, nous devons ajouter une autre dépendance à pom.xml :

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

Nous pouvons vérifier la dernière version de JMH dans Central Maven Repository .

Alternativement, nous pouvons utiliser le plugin maven de JMH pour générer un exemple de projet:

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

Ensuite, créons une méthode main :

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

Maintenant, nous devons créer une méthode et l’annoter avec l’annotation @ Benchmark de JMH:

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

Dans cette méthode init , nous devons écrire du code qui doit être exécuté de manière répétée afin de nous échauffer.

** 7. Indice de performance

**

Au cours des 20 dernières années, la plupart des contributions à Java étaient liées au GC (Garbage Collector) et au JIT (Just In Time Compiler). Presque tous les tests de performances trouvés en ligne sont effectués sur une machine virtuelle déjà en exploitation depuis un certain temps. cependant,

Cependant, Beihang University a publié un rapport de référence tenant compte du temps de préchauffage de la JVM. Ils ont utilisé des systèmes basés sur Hadoop et Spark pour traiter des données volumineuses:

Ici, HotTub désigne l’environnement dans lequel la JVM a été réchauffée.

Comme vous pouvez le constater, l’accélération peut être importante, en particulier pour les opérations de lecture relativement petites - c’est pourquoi ces données sont intéressantes à prendre en compte.

8. Conclusion

Dans cet article rapide, nous avons montré comment la machine virtuelle Java charge les classes au démarrage d’une application et comment nous pouvons réchauffer la machine virtuelle Java pour améliorer ses performances.

Ce livre contient plus d’informations et de directives sur le sujet si vous souhaitez continuer.

Et, comme toujours, le code source complet est disponible over sur GitHub .