JVM(Java Virtual Machine) agit comme un moteur d’exécution pour exécuter les applications Java. La JVM est celle qui appelle réellement la méthode principale présente dans un code java. La JVM fait partie de JRE(Java Runtime Environment).
Les applications Java sont appelées WORA (Write Once Run Anywhere). Cela signifie qu’un programmeur peut développer un code Java sur un système et s’attendre à ce qu’il fonctionne sur n’importe quel autre système compatible Java sans aucun ajustement. Tout cela est possible grâce à la JVM.
Lorsque nous compilons un fichier .java, des fichiers .class(contient du byte-code) avec les mêmes noms de classe présents dans le fichier .java sont générés par le compilateur Java. Ce fichier .class passe par différentes étapes lorsque nous l’exécutons. Ces étapes décrivent ensemble l’ensemble de la JVM.
Sous-système de chargement de classe
Il est principalement responsable de trois activités.
- Chargement
- Liaison
- Initialisation
Chargement : Le chargeur de classe lit le fichier « .class », génère les données binaires correspondantes et les enregistre dans la zone de méthode. Pour chaque fichier « .class », la JVM enregistre les informations suivantes dans la zone des méthodes.
- Le nom entièrement qualifié de la classe chargée et de sa classe parente immédiate.
- Si le fichier « .class » est lié à une classe ou à une interface ou à une Enum.
- Informations sur les modificateurs, les variables et les méthodes, etc.
Après avoir chargé le fichier « .class », la JVM crée un objet de type Class pour représenter ce fichier dans la mémoire du tas. Veuillez noter que cet objet est de type Class prédéfini dans le paquet java.lang. Ces objets Class peuvent être utilisés par le programmeur pour obtenir des informations au niveau de la classe, comme le nom de la classe, le nom du parent, les méthodes et les informations sur les variables, etc. Pour obtenir cette référence d’objet, nous pouvons utiliser la méthode getClass() de la classe Object.
import
java.lang.reflect.Field;
. import
java.lang.reflect.Method;
public
class
Test {
public
static
void
main(String args)
{
Student s1 =
new
Student();
Class c1 = s1.getClass();
System.out.println(c1.getName());
Method m = c1.getDeclaredMethods();
for
(Method method : m)
System.out.println(method.getName());
Field f = c1.getDeclaredFields();
for
(Field field : f)
System.out.println(field.getName());
}
}
private
String name;
private
int
roll_No;
public
String getName() {
return
name; }
public
this
.name = name; }
public
int
getRoll_no() {
return
roll_No; }
public
void
int
roll_no)
{
this
.roll_No = roll_no;
}
}
.
StudentgetNamesetNamegetRoll_nosetRoll_nonameroll_No
Note : Pour chaque fichier « .class » chargé, un seul objet de la classe est créé.
Student s2 = new Student();// c2 will point to same object where // c1 is pointingClass c2 = s2.getClass();System.out.println(c1==c2); // true
La liaison : Effectue la vérification, la préparation et (facultativement) la résolution.
- Vérification : Elle assure l’exactitude du fichier .class c’est-à-dire qu’elle vérifie si ce fichier est correctement formaté et généré par un compilateur valide ou non. Si la vérification échoue, on obtient une exception d’exécution java.lang.VerifyError. Cette activité est effectuée par le composant ByteCodeVerifier. Une fois cette activité terminée alors le fichier de classe est prêt pour la compilation.
- Préparation : La JVM alloue de la mémoire pour les variables de classe et initialise la mémoire aux valeurs par défaut.
- Résolution : C’est le processus de remplacement des références symboliques du type par des références directes. Il est fait en cherchant dans la zone de la méthode pour localiser l’entité référencée.
Initialisation : Dans cette phase, toutes les variables statiques sont affectées avec leurs valeurs définies dans le code et le bloc statique(le cas échéant). Cette opération est exécutée de haut en bas dans une classe et de parent à enfant dans la hiérarchie des classes.
En général, il existe trois chargeurs de classe :
- Chargeur de classe bootstrap : Chaque implémentation de JVM doit avoir un chargeur de classe bootstrap, capable de charger des classes de confiance. Il charge les classes de l’API java de base présentes dans le répertoire « JAVA_HOME/jre/lib ». Ce chemin est communément appelé « bootstrap path ». Il est implémenté dans des langages natifs comme C, C++.
- Chargeur de classes d’extension : C’est un enfant du chargeur de classes bootstrap. Il charge les classes présentes dans les répertoires d’extensions « JAVA_HOME/jre/lib/ext »(Extension path) ou tout autre répertoire spécifié par la propriété système java.ext.dirs. Il est implémenté en java par la classe sun.misc.Launcher$ExtClassLoader.
- Chargeur de classe système/application : C’est un enfant du chargeur de classes d’extension. Il est responsable du chargement des classes à partir du classpath de l’application. Il utilise en interne la variable d’environnement qui est mappée à java.class.path. Il est également implémenté en Java par la classe sun.misc.Launcher$AppClassLoader.
public
class
Test {
public
static
void
main(String args)
{
System.out.println(String.
class
.getClassLoader());
System.out.println(Test.
class
.getClassLoader());
}
}
nulljdk.internal.loader.ClassLoaders$AppClassLoader@8bcc55f
Note : La JVM suit le principe de délégation-hiérarchie pour charger les classes. Le chargeur de classes système délègue la demande de chargement au chargeur de classes d’extension et le chargeur de classes d’extension délègue la demande au chargeur de classes bootstrap. Si une classe est trouvée dans le chemin du bootstrap, la classe est chargée, sinon la demande est à nouveau transférée au chargeur de classe d’extension, puis au chargeur de classe système. Enfin, si le chargeur de classe système ne parvient pas à charger la classe, alors nous obtenons une exception d’exécution java.lang.ClassNotFoundException.
Mémoire JVM
- Zone des méthodes : Dans la zone de méthode, toutes les informations de niveau classe comme le nom de la classe, le nom de la classe parente immédiate, les informations sur les méthodes et les variables, etc. sont stockées, y compris les variables statiques. Il n’y a qu’une seule zone de méthodes par JVM, et c’est une ressource partagée.
- Zone de tas : Les informations de tous les objets sont stockées dans la zone de tas. Il existe également une seule zone de tas par JVM. C’est également une ressource partagée.
- Zone de pile : Pour chaque thread, la JVM crée une pile d’exécution qui est stockée ici. Chaque bloc de cette pile est appelé enregistrement d’activation/stack frame qui stocke les appels de méthodes. Toutes les variables locales de cette méthode sont stockées dans leur cadre correspondant. Après la terminaison d’un thread, sa pile d’exécution sera détruite par la JVM. Il ne s’agit pas d’une ressource partagée.
- Registres PC : Stocke l’adresse de l’instruction d’exécution actuelle d’un thread. De toute évidence, chaque thread a des registres PC distincts.
- Pile de méthodes natives : Pour chaque thread, une pile native distincte est créée. Elle stocke les informations relatives aux méthodes natives.
Moteur d’exécution
Le moteur d’exécution exécute le « .class » (bytecode). Il lit le byte-code ligne par ligne, utilise les données et informations présentes dans les différentes zones de mémoire et exécute les instructions. Il peut être classé en trois parties :
- Interprète : Il interprète le bytecode ligne par ligne et exécute ensuite. L’inconvénient ici est que lorsqu’une méthode est appelée plusieurs fois, chaque fois l’interprétation est nécessaire.
- Compilateur Just-In-Time(JIT) : Il est utilisé pour augmenter l’efficacité d’un interprète. Il compile l’ensemble du bytecode et le modifie en code natif de sorte que chaque fois que l’interprète voit des appels de méthode répétés, le JIT fournit du code natif direct pour cette partie, de sorte que la réinterprétation n’est pas nécessaire, donc l’efficacité est améliorée.
- Garbage Collector : Il détruit les objets non référencés. Pour en savoir plus sur le Garbage Collector, reportez-vous à Garbage Collector.
Java Native Interface (JNI) :
C’est une interface qui interagit avec les bibliothèques de méthodes natives et fournit les bibliothèques natives(C, C++) nécessaires à l’exécution. Elle permet à la JVM d’appeler des bibliothèques C/C++ et d’être appelée par des bibliothèques C/C++ qui peuvent être spécifiques au matériel.
Les bibliothèques de méthodes natives :
C’est une collection des bibliothèques natives(C, C++) qui sont requises par le moteur d’exécution.
Cet article est contribué par Gaurav Miglani. Si vous aimez GeeksforGeeks et que vous souhaitez contribuer, vous pouvez également écrire un article en utilisant contribute.geeksforgeeks.org ou envoyer votre article par courrier à [email protected]. Voyez votre article apparaître sur la page principale de GeeksforGeeks et aidez d’autres Geeks.
Veuillez écrire des commentaires si vous trouvez quelque chose d’incorrect, ou si vous voulez partager plus d’informations sur le sujet abordé ci-dessus.