JavaRush /Blog Java /Random-FR /Compiler et exécuter des applications Java sous le capot
Павел Голов
Niveau 34
Москва

Compiler et exécuter des applications Java sous le capot

Publié dans le groupe Random-FR

Contenu:

  1. Introduction
  2. Compilation en bytecode
  3. Exemple de compilation et d'exécution de programme
  4. Exécuter un programme sur une machine virtuelle
  5. Compilation juste à temps (JIT)
  6. Conclusion
Compiler et exécuter des applications Java sous le capot - 1

1. Introduction

Salut tout le monde! Aujourd'hui, j'aimerais partager mes connaissances sur ce qui se passe sous le capot de la JVM (Java Virtual Machine) après l'exécution d'une application écrite en Java. De nos jours, il existe des environnements de développement à la mode qui vous aident à éviter de penser aux composants internes de la JVM, à la compilation et à l'exécution du code Java, ce qui peut amener les nouveaux développeurs à manquer ces aspects importants. Parallèlement, des questions sur ce sujet sont souvent posées lors des entretiens, c'est pourquoi j'ai décidé d'écrire un article.

2. Compilation en bytecode

Compiler et exécuter des applications Java sous le capot - 2
Commençons par la théorie. Lorsque nous écrivons une application, nous créons un fichier avec une extension .javaet y plaçons du code dans le langage de programmation Java. Un tel fichier contenant du code lisible par l'homme est appelé fichier de code source . Une fois le fichier de code source prêt, vous devez l’exécuter ! Mais au stade, il contient des informations compréhensibles uniquement par les humains. Java est un langage de programmation multiplateforme. Cela signifie que les programmes écrits en Java peuvent être exécutés sur n'importe quelle plate-forme sur laquelle un système d'exécution Java dédié est installé. Ce système s'appelle Java Virtual Machine (JVM). Afin de traduire un programme du code source en code que la JVM peut comprendre, vous devez le compiler. Le code que la JVM comprend est appelé bytecode et contient un ensemble d'instructions que la machine virtuelle exécutera ensuite. Pour compiler le code source en bytecode, il existe un compilateur javacinclus dans le JDK (Java Development Kit). En entrée, le compilateur accepte un fichier avec l'extension .java, contenant le code source du programme, et en sortie, il produit un fichier avec l'extension .class, contenant le bytecode nécessaire à l'exécution du programme par la machine virtuelle. Une fois qu'un programme a été compilé en bytecode, il peut être exécuté à l'aide d'une machine virtuelle.

3. Exemple de compilation et d'exécution de programme

Supposons que nous ayons un programme simple, contenu dans un fichier Calculator.java, qui prend 2 arguments numériques de ligne de commande et affiche le résultat de leur addition :
class Calculator {
    public static void main(String[] args){
        int a = Integer.valueOf(args[0]);
        int b = Integer.valueOf(args[1]);

        System.out.println(a + b);
    }
}
Afin de compiler ce programme en bytecode, nous utiliserons le compilateur javacen ligne de commande :
javac Calculator.java
Après compilation, nous recevons un fichier avec le bytecode en sortie Calculator.class, que nous pouvons exécuter en utilisant la machine java installée sur notre ordinateur à l'aide de la commande java sur la ligne de commande :
java Calculator 1 2
Notez qu'après le nom du fichier, 2 arguments de ligne de commande ont été spécifiés - les numéros 1 et 2. Après l'exécution du programme, le numéro 3 sera affiché sur la ligne de commande. Dans l'exemple ci-dessus, nous avions une classe simple qui vit de manière autonome . Mais que se passe-t-il si la classe est dans un package ? Simulons la situation suivante : créons des répertoires src/ru/javarushet y plaçons notre classe. Cela ressemble maintenant à ceci (nous avons ajouté le nom du package au début du fichier) :
package ru.javarush;

class Calculator {
    public static void main(String[] args){
        int a = Integer.valueOf(args[0]);
        int b = Integer.valueOf(args[1]);

        System.out.println(a + b);
    }
}
Compilons une telle classe avec la commande suivante :
javac -d bin src/ru/javarush/Calculator.java
Dans cet exemple, nous avons utilisé une option supplémentaire du compilateur -d binqui place les fichiers compilés dans un répertoire binavec une structure similaire au répertoire src, mais le répertoire bindoit être créé à l'avance. Cette technique est utilisée pour éviter de confondre les fichiers de code source avec les fichiers de bytecode. Avant d'exécuter le programme compilé, il convient d'expliquer le concept classpath. Classpathest le chemin par rapport auquel la machine virtuelle recherchera les packages et les classes compilées. Autrement dit, nous indiquons ainsi à la machine virtuelle quels répertoires du système de fichiers sont la racine de la hiérarchie des packages Java. Classpathpeut être spécifié lors du démarrage du programme à l'aide du drapeau -classpath. On lance le programme à l'aide de la commande :
java -classpath ./bin ru.javarush.Calculator 1 2
Dans cet exemple, nous avions besoin du nom complet de la classe, y compris le nom du package dans lequel elle réside. L'arborescence des fichiers finale ressemble à ceci :
├── src
│     └── ru
│          └── javarush
│                  └── Calculator.java
└── bin
      └── ru
           └── javarush
                   └── Calculator.class

4. Exécution du programme par une machine virtuelle

Nous avons donc lancé le programme écrit. Mais que se passe-t-il lorsqu’un programme compilé est lancé par une machine virtuelle ? Voyons d’abord ce que signifient les concepts de compilation et d’interprétation de code. La compilation est la traduction d'un programme écrit dans un langage source de haut niveau en un programme équivalent dans un langage de bas niveau similaire au code machine. L'interprétation est une analyse, un traitement et une exécution immédiate opérateur par instruction (commande par ligne, ligne par ligne) du programme source ou de la requête (par opposition à la compilation, dans laquelle le programme est traduit sans l'exécuter). Le langage Java possède à la fois un compilateur ( javac) et un interpréteur, qui est une machine virtuelle qui convertit le bytecode en code machine ligne par ligne et l'exécute immédiatement. Ainsi, lorsque nous exécutons un programme compilé, la machine virtuelle commence à l'interpréter, c'est-à-dire à convertir ligne par ligne le bytecode en code machine, ainsi qu'à son exécution. Malheureusement, l'interprétation pure du bytecode est un processus assez long et rend Java lent par rapport à ses concurrents. Pour éviter cela, un mécanisme a été introduit pour accélérer l'interprétation du bytecode par la machine virtuelle. Ce mécanisme est appelé compilation juste à temps (JITC).

5. Compilation juste à temps (JIT)

En termes simples, le mécanisme de compilation Just-In-Time est le suivant : s'il y a des parties du code dans le programme qui sont exécutées plusieurs fois, elles peuvent alors être compilées une fois en code machine pour accélérer leur exécution ultérieure. Après avoir compilé une telle partie du programme en code machine, à chaque appel ultérieur à cette partie du programme, la machine virtuelle exécutera immédiatement le code machine compilé plutôt que de l'interpréter, ce qui accélérera naturellement l'exécution du programme. L'accélération du programme est obtenue en augmentant la consommation de mémoire (nous devons stocker le code machine compilé quelque part !) et en augmentant le temps passé à la compilation lors de l'exécution du programme. La compilation JIT est un mécanisme plutôt complexe, alors allons-y. Il existe 4 niveaux de compilation JIT du bytecode en code machine. Plus le niveau de compilation est élevé, plus il est complexe, mais en même temps l'exécution d'une telle section sera plus rapide qu'une section de niveau inférieur. JIT - Le compilateur décide du niveau de compilation à définir pour chaque fragment de programme en fonction de la fréquence d'exécution de ce fragment. Sous le capot, la JVM utilise 2 compilateurs JIT – C1 et C2. Le compilateur C1 est également appelé compilateur client et est capable de compiler du code uniquement jusqu'au 3ème niveau. Le compilateur C2 est responsable du 4ème niveau de compilation, le plus complexe et le plus rapide.
Compiler et exécuter des applications Java sous le capot - 3
De ce qui précède, nous pouvons conclure que pour les applications clientes simples, il est plus rentable d'utiliser le compilateur C1, car dans ce cas, la rapidité avec laquelle l'application démarre est importante pour nous. Les applications côté serveur à longue durée de vie peuvent prendre plus de temps à démarrer, mais à l'avenir, elles devront fonctionner et remplir leur fonction rapidement - ici, le compilateur C2 nous convient. Lors de l'exécution d'un programme Java sur la version x32 de la JVM, nous pouvons spécifier manuellement le mode que nous souhaitons utiliser à l'aide des indicateurs -clientet -server. Lorsque cet indicateur est spécifié, -clientla JVM n'effectuera pas d'optimisations complexes du bytecode, ce qui accélérera le temps de démarrage de l'application et réduira la quantité de mémoire consommée. Lors de la spécification de l'indicateur, -serverl'application mettra plus de temps à démarrer en raison d'optimisations complexes du bytecode et utilisera plus de mémoire pour stocker le code machine, mais le programme s'exécutera plus rapidement à l'avenir. Dans la version x64 de la JVM, l'indicateur -clientest ignoré et la configuration du serveur d'applications est utilisée par défaut.

6. Conclusion

Ceci conclut mon bref aperçu du fonctionnement de la compilation et de l’exécution d’une application Java. Points principaux:
  1. Le compilateur javac convertit le code source d'un programme en bytecode pouvant être exécuté sur n'importe quelle plate-forme sur laquelle la machine virtuelle Java est installée ;
  2. Après compilation, la JVM interprète le bytecode résultant ;
  3. Pour accélérer les applications Java, la JVM utilise un mécanisme de compilation juste à temps qui convertit les sections les plus fréquemment exécutées d'un programme en code machine et les stocke en mémoire.
J'espère que cet article vous a aidé à mieux comprendre le fonctionnement de notre langage de programmation préféré. Merci d'avoir lu, les critiques sont les bienvenues !
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION