Maison >Java >javaDidacticiel >Introduction aux points de connaissance de la machine virtuelle Java (JVM)
1. Qu'est-ce qu'une machine virtuelle Java ? Lorsque vous parlez d'une machine virtuelle Java, vous faites peut-être référence à :
1. Une spécification abstraite de machine virtuelle Java
2. Une implémentation de machine virtuelle Java spécifique. machine Instance de machine
2. Cycle de vie de la machine virtuelle Java
Une machine virtuelle Java en cours d'exécution a une tâche claire : exécuter des programmes Java. Il s'exécute lorsque le programme commence à s'exécuter et s'arrête lorsque le programme se termine. Si vous exécutez trois programmes sur la même machine, trois machines virtuelles Java seront en cours d'exécution.
La machine virtuelle Java démarre toujours par une méthode main(). Cette méthode doit être publique, renvoyer void et accepter directement un tableau de chaînes. Lorsque le programme est exécuté, vous devez spécifier le nom de classe qui encapsule la méthode main() dans la machine virtuelle Java.
La méthode Main() est le point de départ du programme, et le thread dans lequel il est exécuté est initialisé comme thread initial du programme. Tous les autres threads du programme sont lancés par lui. Il existe deux types de threads en Java : les threads démons et les threads non démons. Le thread démon est un thread utilisé par la machine virtuelle Java elle-même. Par exemple, le thread responsable du garbage collection est un thread démon. Bien sûr, vous pouvez également définir votre propre programme comme thread démon. Le thread initial contenant la méthode Main() n’est pas un thread démon.
Tant que des threads ordinaires s'exécutent dans la machine virtuelle Java, la machine virtuelle Java ne s'arrêtera pas. Si vous disposez des autorisations suffisantes, vous pouvez terminer le programme en appelant la méthode exit().
3. Architecture de la machine virtuelle Java
Une série de sous-systèmes, de zones de mémoire, de types de données et de directives d'utilisation sont définis dans les spécifications de la machine virtuelle Java. Ces composants constituent la structure interne de la machine virtuelle Java. Ils fournissent non seulement une structure interne claire pour la mise en œuvre de la machine virtuelle Java, mais régulent également strictement le comportement externe de la mise en œuvre de la machine virtuelle Java.
Chaque machine virtuelle Java possède un sous-système de chargement de classes (sous-système de chargement de classes), qui est responsable du chargement des types (classes et interfaces) dans le programme et de leur donner des noms uniques. Chaque machine virtuelle Java dispose d'un moteur d'exécution (execution engine) chargé d'exécuter les instructions contenues dans les classes chargées.
L'exécution du programme nécessite une certaine quantité d'espace mémoire, comme le bytecode, d'autres informations supplémentaires sur la classe chargée, les objets du programme, les paramètres de méthode, les valeurs de retour, les variables locales, les variables intermédiaires traitées, etc. La machine virtuelle Java stocke toutes ces informations dans des zones de données. Bien que chaque implémentation de machine virtuelle Java comprenne une zone de données, les dispositions de la spécification de machine virtuelle Java concernant la zone de données sont très abstraites. De nombreux détails structurels sont laissés aux implémenteurs de machines virtuelles Java. Les structures de mémoire sur les différentes implémentations de machines virtuelles Java varient considérablement. Certaines implémentations peuvent utiliser beaucoup de mémoire, tandis que d'autres peuvent utiliser très peu de mémoire ; certaines implémentations peuvent utiliser de la mémoire virtuelle et d'autres non. Cette spécification de mémoire de machine virtuelle Java relativement raffinée permet à la machine virtuelle Java d'être implémentée sur un large éventail de plates-formes.
Une partie de la zone de données est commune à l'ensemble du programme et d'autres parties sont contrôlées par des threads distincts. Chaque machine virtuelle Java contient une zone de méthode et un tas, qui sont partagés par l'ensemble du programme. Une fois que la machine virtuelle Java a chargé et analysé une classe, elle enregistre les informations analysées à partir du fichier de classe dans la zone des méthodes. Les objets créés lors de l'exécution du programme sont stockés dans le tas.
Lorsqu'un thread est créé, il se verra attribuer son propre registre PC "pc register" (compteur de programme) et sa propre pile Java (pile Java). Lorsque le thread n'utilise pas la méthode native, le registre PC stocke la prochaine instruction exécutée par le thread. La pile Java enregistre l'état lorsqu'un thread appelle une méthode, y compris les variables locales, les paramètres de la méthode appelante, les valeurs de retour et les variables intermédiaires traitées. L'état lorsqu'une méthode native est appelée est stocké dans des piles de méthodes natives, éventuellement dans des registres ou dans une autre mémoire non indépendante de la plate-forme.
La pile Java est composée de stack frames (ou frames). Le bloc de pile contient l'état des appels de méthode Java. Lorsqu'un thread appelle une méthode, la machine virtuelle Java place un nouveau bloc sur la pile Java. Lorsque la méthode se termine, la machine virtuelle Java affiche le bloc correspondant et le supprime.
La machine virtuelle Java n'utilise pas de registres pour stocker les résultats intermédiaires des calculs, mais utilise la pile Java pour stocker les résultats intermédiaires. Cela rend les instructions de la machine virtuelle Java plus compactes et facilite l'implémentation de la machine virtuelle Java sur un périphérique sans registres.
Dans la pile Java de l'image, le thread trois du registre PC est gris à mesure qu'il grandit vers le bas car il exécute une méthode locale et sa prochaine instruction d'exécution n'est pas enregistrée dans le registre PC.
4. Types de données
Toutes les données utilisées dans la machine virtuelle Java ont un certain type de données et les opérations sont strictement définies dans la spécification de la machine virtuelle Java. Les types de données en Java sont divisés en types primitifs (types primitifs) et types de données de référence (types de référence). Les types de référence dépendent de l'objet réel, mais pas de l'objet lui-même. Les types de données primitifs ne dépendent de rien ; ils représentent les données elles-mêmes.
Tous les types de données primitifs du langage de programmation Java sont des types de données primitifs de la machine virtuelle Java, à l'exception des booléens. Lorsque le compilateur compile le code source Java dans son propre code, il utilise le type entier (int) ou le type octet (byte) pour représenter le type booléen. La machine virtuelle Java utilise l'entier 0 pour représenter le booléen faux et un entier non nul pour représenter le booléen vrai. Les tableaux booléens sont représentés sous forme de tableaux d'octets, bien qu'ils puissent être stockés dans des tableaux d'octets ou des champs de bits dans le tas.
À l'exception du booléen, les autres types primitifs du langage Java sont des types de données dans la machine virtuelle Java. En Java, les types de données sont divisés en : octet entier, short, int, long ; type float et virgule flottante, double. Les types de données dans le langage Java ont la même portée sur n'importe quel hôte.
Dans la machine virtuelle Java, il existe également un type de valeur de retour de type de données primitif (returnValue) qui ne peut pas être utilisé dans le langage Java. Ce type est utilisé pour implémenter les « clauses finales » dans les programmes Java. Pour plus de détails, voir « Clauses finales » au chapitre 18.
Les types de référence peuvent être créés comme : type de classe, type d'interface, type de tableau. Ils font tous référence à des objets créés dynamiquement. Lorsqu'un type référence fait référence à null, cela signifie qu'aucun objet n'est référencé.
La spécification de la machine virtuelle Java définit uniquement la plage représentée par chaque type de données, mais ne définit pas l'espace occupé par chaque type lors du stockage. La manière dont ils sont stockés dépend de l’implémenteur de la machine virtuelle Java. Pour plus d'informations sur les types à virgule flottante, consultez le Chapitre 14, « Arithmétique à virgule flottante ».
TypeRange
byte entier signé en complément à deux de 8 bits (-27 à 27 - 1, inclus)
short entier signé en complément à deux de 16 bits (-215 à 215 - 1, inclus)
int entier signé en complément à deux de 32 bits (-231 à 231 - 1, inclus)
long entier signé en complément à deux de 64 bits (-263 à 263 - 1, inclus)
charCaractère Unicode non signé de 16 bits (0 à 216 - 1, inclus)
floatFlotteur simple précision IEEE 754 de 32 bits
double flotteur double précision IEEE 754 64 bits
valeur de retouradresse d'un opcode dans la même méthode
référence à un objet sur le tas, ou null
5 longueur d'octet
Le plus petit mot d'unité de données (mot) dans la machine virtuelle Java, dont la taille est définie par l'implémenteur de la machine virtuelle Java. Mais la taille d'un mot doit être suffisante pour accueillir byte, short, int, char, float, returnValue, reference, deux mots doivent suffire pour accueillir long, double ; Par conséquent, le développeur de la machine virtuelle doit au moins fournir un mot inférieur à 31 bits, mais il est préférable de choisir la longueur de mot la plus efficace sur la plate-forme spécifique.
Au moment de l'exécution, un programme Java ne peut pas déterminer la longueur des mots de la machine sur laquelle il s'exécute. La longueur des mots n'affecte pas le comportement du programme, c'est juste un moyen d'expression dans la machine virtuelle Java.
6. Sous-système de chargeur de classe
Il existe deux types de chargeurs de classe dans la machine virtuelle Java : le chargeur de classe primordial et les objets chargeur de classe. Le chargeur de classe d'origine fait partie de l'implémentation de la machine virtuelle Java et l'objet chargeur de classe fait partie du programme en cours d'exécution. Les classes chargées par différents chargeurs de classes sont séparées par différents espaces de noms.
Le chargeur de classes appelle de nombreuses autres parties de la machine virtuelle Java et de nombreuses classes du package java.lang. Par exemple, l'objet de chargement de classe est une instance de la sous-classe java.lang.ClassLoader. Les méthodes de la classe ClassLoader peuvent accéder au mécanisme de chargement de classe dans la machine virtuelle. Chaque classe chargée par la machine virtuelle Java sera représentée comme un Java ; .lang.Class Instance de classe. Comme les autres objets, les objets chargeur de classe et les objets Classe sont stockés dans le tas et les informations chargées sont stockées dans la zone méthode.
1. Chargement, liaison et initialisation
Le sous-système de chargement de classe n'est pas seulement responsable de la localisation et du chargement des fichiers de classe, il fait bien d'autres choses selon les étapes strictes suivantes : (Pour des informations spécifiques, voir le chapitre 7 « Cycle de vie des classes »).
1), Chargement : rechercher et importer des informations binaires du type spécifié (classe et interface)
2), Connexion : vérifier, préparer et analyser
①Vérification : garantir l'exactitude du type importé
②Préparation : allouer de la mémoire pour le type et initialisez-le à la valeur par défaut
③Parsing : analysez la référence de caractère pour boire directement
3), Initialisation : appelez le code Java et initialisez la variable de classe à la valeur appropriée
2 Le chargeur de classe primordial)
Chaque machine virtuelle Java doit implémenter. un chargeur de classe primitif qui peut charger des classes conformes au format de fichier de classe et fiables. Cependant, la spécification de la machine virtuelle Java ne définit pas la manière de charger les classes, ce qui relève de la décision de l'implémenteur de la machine virtuelle Java. Pour un type avec un nom de type donné, le chargeur d'origine doit trouver le fichier avec le nom de type plus ".class" et le charger dans la machine virtuelle.
3. Objet chargeur de classe
Bien que l'objet chargeur de classe fasse partie du programme Java, les trois méthodes de la classe ClassLoader peuvent accéder au sous-système de chargement de classe dans la machine virtuelle Java.
1), Class final protégé definitionClass(...) : utilisez cette méthode pour accéder à un tableau d'octets et définir un nouveau type.
2), Class protégé findSystemClass(String name) : Charge la classe spécifiée Si elle a été chargée, retournez-la directement.
3), protected final void solveClass(Class c) : La méthode definitionClass() charge simplement une classe. Cette méthode est responsable de la connexion dynamique et de l'initialisation ultérieures.
Pour des informations spécifiques, voir le chapitre 8 « Le modèle de liaison ».
4. Espace de noms
Lorsque plusieurs chargeurs de classe chargent la même classe, afin de garantir l'unicité de leurs noms, l'identifiant du chargeur de classe qui charge la classe doit être ajouté avant le nom de la classe. Pour des informations spécifiques, voir le chapitre 8 « Le modèle de liaison ».
7. La zone méthode
Dans la machine virtuelle Java, les informations du type chargé sont stockées dans la zone méthode. La forme d'organisation de ces informations en mémoire est définie par l'implémenteur de la machine virtuelle. Par exemple, si la machine virtuelle fonctionne sur un processeur « little-endian », elle peut enregistrer les informations au format « little-endian ». Dans les fichiers de classe Java, ils sont enregistrés au format « big-endian ». Les concepteurs peuvent stocker les données dans un format de représentation le mieux adapté à la machine locale afin de garantir que le programme peut s'exécuter à la vitesse la plus rapide. Cependant, sur un appareil doté de seulement une petite quantité de mémoire, l’implémenteur de la machine virtuelle n’occupera pas une grande quantité de mémoire.
Tous les threads du programme partagent une zone de méthode, la méthode d'accès aux informations de la zone de méthode doit donc être thread-safe. Si vous avez deux threads chargeant une classe appelée Lava, un seul thread est autorisé à charger cette classe et l'autre doit attendre.
Lorsque le programme est en cours d'exécution, la taille de la zone de méthode est variable et le programme peut être développé pendant son exécution. Certaines implémentations de machines virtuelles Java peuvent également personnaliser la taille initiale, les valeurs minimales et maximales de la zone de méthode via des paramètres.
La zone de méthode peut également être collectée. Étant donné que le contenu du programme est chargé dynamiquement par le chargeur de classes, toutes les classes peuvent ne plus être référencées. Lorsqu'une classe atteint cet état, elle peut être récupérée. Les classes qui ne sont pas chargées incluent deux états, l'un n'est vraiment pas chargé et l'autre est l'état "non référencé". Voir La durée de vie d'une classe au chapitre 7 pour plus de détails.
1. Informations sur le type
Chaque type chargé enregistrera les informations suivantes dans la zone de méthode de la machine virtuelle Java :
1), le nom complet du type
2), le nom complet de la superclasse directe du type (sauf si il n'y a pas de type parent, ou la forme Frey est java.lang.Object) (Le nom complet de la superclasse directe du type)
3), si le type donné est une classe ou une interface (classe ou une interface) (Que ce soit ou le type n'est pas une classe)
4), les modificateurs de type (public, privé, protégé, statique, final, volatile, transitoire, etc.) (Les modificateurs de type)
5), toutes les interfaces parents Une liste ordonnée des éléments pleinement qualifiés noms de toutes les superinterfaces directes. La structure de données enregistrée par le nom complet du type est définie par l'implémenteur de la machine virtuelle. De plus, la machine virtuelle Java stocke également les informations suivantes pour chaque type :
1), le pool de constantes pour le type (Le pool de constantes pour le type)
2), les informations sur le champ de type (Informations sur le champ)
3), Type des informations sur la méthode (Informations sur la méthode)
4), toutes les variables de classe (statiques) déclarées dans le type, à l'exception des constantes (Toutes les variables de classe (statiques) déclarées dans le type, à l'exception des constantes)
5), une référence au chargeur de classe (A référence à la classe ClassLoader)
6), une référence à Class Class (Une référence à la classe Class)
1) Le pool de constantes pour le type (Le pool de constantes pour le type)
Tous les types enregistrés dans le pool de constantes sont des collections ordonnées de constantes, y compris des constantes directes (littéraux) telles que des chaînes, des entiers et des constantes à virgule flottante et. références symboliques aux types, champs et méthodes. Chaque constante enregistrée dans le pool de constantes possède un index, tout comme un champ dans un tableau. Étant donné que le pool de constantes stocke les références de caractères aux types, champs et méthodes utilisés par tous les types du pool de constantes, il constitue également l'objet principal de la connexion dynamique. Voir le chapitre 6, « Le fichier de classe Java » pour plus de détails.
2), saisissez les informations sur le champ (Informations sur le champ)
Nom du champ, type de champ, modificateurs de champ (public, privé, protégé, statique, final, volatile, transitoire, etc.) et l'ordre dans lequel les champs sont définis dans le classe.
3), saisissez les informations sur la méthode (Informations sur la méthode)
Nom de la méthode, type de valeur de retour de la méthode (ou void), nombre de paramètres de méthode, types et leur ordre, modificateurs de champ (public, privé, protégé, statique, final, volatile, transitoire , etc.), l'ordre dans lequel les méthodes sont définies dans la classe
Si elle n'est pas abstraite et locale, cette méthode doit également sauvegarder
le bytecode de la méthode, la taille de la pile d'opérandes de la méthode et la taille de la zone des variables locales (des informations légèrement détaillées peuvent être fournies), la liste des exceptions (voir le chapitre 17 "Exceptions" pour plus de détails.)
4), les variables de classe (statiques) (Variables de classe)
Les variables de classe sont partagées par toutes les instances de la classe , même si ce n'est pas via des instances de la classe Également accessible. Ces variables sont liées à la classe (plutôt qu'aux instances de la classe), elles font donc partie des données logiques de la classe. Avant que la machine virtuelle Java n'utilise cette classe, vous devez allouer de la mémoire pour la variable de classe (non finale). La méthode de traitement de la constante (finale) est différente de cette variable de classe (non finale). Lorsque chaque type utilise une constante, il la copie dans son propre pool de constantes. Les constantes sont également stockées dans la zone des méthodes comme les variables de classe, sauf qu'elles sont stockées dans le pool de constantes. (Probablement, les variables de classe sont partagées par toutes les instances, tandis que le pool constant est unique à chaque instance). Les variables de classe non finales sont enregistrées dans le cadre des données pour le type qui les déclare, tandis que les constantes finales sont enregistrées dans le cadre des données pour tout type qui les utilise. Pour plus de détails, voir le chapitre 6 "Le fichier de classe JavaLe fichier de classe Java"
5), Une référence à la classe ClassLoader
Pour chaque type chargé par la machine virtuelle Java, la machine virtuelle doit enregistrer si ce type est un chargeur de classe d'origine ou un chargeur de classe. chargement. Les types chargés par un chargeur de classe doivent conserver une référence au chargeur de classe. Ces informations sont utilisées lorsque le chargeur de classe se connecte dynamiquement. Lorsqu'une classe fait référence à une autre classe, la machine virtuelle doit enregistrer que le type référencé est chargé par le même chargeur de classe. C'est également le processus par lequel la machine virtuelle gère différents espaces de noms. Pour plus de détails, voir le chapitre 8 « Le modèle de liaison »
6), Une référence à la classe Class
La machine virtuelle Java crée une instance de la classe java.lang.Class pour chaque type chargé. Vous pouvez également utiliser la méthode de classe Class :
public static Class forName(String className) pour rechercher ou charger une classe et obtenir une instance de la classe Class correspondante. Grâce à cette instance de la classe Class, nous pouvons accéder aux informations dans la zone méthode de la machine virtuelle Java. Pour plus de détails, reportez-vous au JavaDoc de la classe Class.
2. Tableaux de méthodes
Afin d'accéder plus efficacement à toutes les données stockées dans la zone de méthode, la structure de stockage de ces données doit être soigneusement conçue. Dans tous les domaines de méthodes, en plus de sauvegarder les informations d'origine ci-dessus, il existe également une structure de données conçue pour accélérer l'accès, comme une liste de méthodes. Pour chaque classe non abstraite chargée, la machine virtuelle Java générera une liste de méthodes pour elle. Cette liste enregistre les références à toutes les méthodes d'instance qui peuvent être appelées par cette classe et signale les erreurs aux méthodes appelées dans la classe parent. Pour plus de détails, voir le chapitre 8 « Le modèle de liaison » 8. Tas
Lorsqu'un programme Java crée une instance ou un tableau d'une classe, il alloue de la mémoire pour le nouvel objet dans le tas. Il n'y a qu'un seul tas dans la machine virtuelle et tous les threads le partagent.
1. Garbage Collection
Le garbage collection est la principale méthode pour libérer des objets non référencés. Il peut également déplacer des objets pour réduire la fragmentation du tas. Le garbage collection n'est pas strictement défini dans la spécification de la machine virtuelle Java, mais il est défini qu'une implémentation d'une machine virtuelle Java doit gérer son propre tas d'une manière ou d'une autre. Voir le chapitre 9 « Garbage Collection » pour plus de détails.
2. Structure de stockage d'objets (représentation d'objet)
Les spécifications de la machine virtuelle Java ne définissent pas la manière dont les objets sont stockés dans le tas. Chaque objet stocke principalement les variables d'objet définies dans sa classe et sa classe parent. Pour une référence à un objet donné, la machine virtuelle doit localiser rapidement les données de cet objet. De plus, une méthode doit être fournie pour référencer les données de l'objet de méthode via l'objet, comme la référence de l'objet dans la zone de méthode, de sorte que les données enregistrées par un objet contiennent souvent un pointeur vers la zone de méthode sous une forme ou une autre.
Une conception de tas possible consiste à diviser le tas en deux parties : un pool de référence et un pool d'objets. Une référence à un objet est un pointeur local vers un pool de référence. Chaque entrée du pool de référence contient deux parties : un pointeur vers les données d'objet dans le pool d'objets et un pointeur vers les données de classe d'objet dans la zone de méthode. Cette conception peut faciliter la défragmentation du tas de la machine virtuelle Java. Lorsque la machine virtuelle déplace un objet dans le pool d'objets, il lui suffit de modifier l'adresse du pointeur dans le pool de référence correspondant. Mais chaque fois que vous accédez aux données de l'objet, vous devez traiter le pointeur deux fois. La figure ci-dessous illustre cette conception de tas. L'applet HeapOfFish du chapitre 9, « Garbage Collection », illustre cette conception.
Une autre conception de tas est la suivante : la référence d'un objet est un pointeur décalé pointant vers une pile de données et pointant vers l'objet correspondant. Cette conception facilite l'accès aux objets, mais le déplacement des objets devient extrêmement compliqué. La figure suivante illustre cette conception
Lorsque le programme tente de convertir un objet en un autre type, la machine virtuelle doit déterminer si la conversion est le type de l'objet ou son type parent. Une chose similaire sera faite lorsque le programme utilisera l'instruction instanceof. Lorsqu'un programme appelle une méthode d'un objet, la machine virtuelle doit effectuer une liaison dynamique et doit déterminer le type de méthode à appeler. Cela nécessite également le jugement ci-dessus.
Quelle que soit la conception utilisée par l'implémenteur de la machine virtuelle, il peut enregistrer des informations similaires à une liste de méthodes pour chaque objet. Étant donné que cela peut augmenter la vitesse des appels de méthode objet, il est très important d'améliorer les performances des machines virtuelles. Cependant, les spécifications des machines virtuelles n'exigent pas que des structures de données similaires soient implémentées. Le diagramme ci-dessous représente cette structure. La figure montre toutes les structures de données associées à une référence d'objet, notamment :
1), un pointeur pour saisir des données
2) et une liste de méthodes d'un objet. Une liste de méthodes est un tableau de pointeurs vers toutes les méthodes pouvant être appelées sur un objet. Les données de méthode comprennent trois parties : la taille de la pile d'opcodes et la zone de variable locale de la pile de méthodes ;
Chaque objet de la machine virtuelle Java doit être associé à un verrou (mutex) utilisé pour synchroniser plusieurs threads. Dans le même temps, un seul objet peut détenir le verrou de cet objet. Lorsqu'une personne possède le verrou de cet objet, elle peut demander le verrou plusieurs fois, mais elle doit également déverrouiller le verrou un nombre de fois correspondant avant de pouvoir réellement libérer le verrou de l'objet. De nombreux objets ne sont pas verrouillés tout au long de leur durée de vie, ces informations ne doivent donc être ajoutées qu'en cas de besoin. De nombreuses implémentations de machines virtuelles Java n'incluent pas de « données de verrouillage » dans les données de l'objet et ne génèrent les données correspondantes qu'en cas de besoin. En plus d'implémenter le verrouillage des objets, chaque objet est également logiquement associé à une implémentation « d'attente ». Le verrouillage aide les threads à traiter les données partagées de manière indépendante sans interférer avec les autres threads. « Wait set » aide les fils de discussion de groupe à collaborer pour atteindre le même objectif. "Wait set" est souvent implémenté via les méthodes wait() et notify() de la classe Object.
Le garbage collection nécessite également des informations indiquant si les objets du tas sont associés. La spécification de la machine virtuelle Java indique que le garbage collection exécute la méthode de finalisation d'un objet une fois, mais permet à la méthode de finalisation de référencer à nouveau l'objet. Lorsque l'objet n'est pas référencé à nouveau, il n'est pas nécessaire d'appeler à nouveau la méthode de finalisation. Par conséquent, la machine virtuelle doit également enregistrer des informations indiquant si la méthode finalize a été exécutée. Pour plus d'informations, voir « Garbage Collection » au chapitre 9
3. Stockage d'un tableau (représentation d'un tableau)
En Java, un tableau est un objet complet. Il est stocké dans le tas comme un objet et possède un pointeur vers une référence de classe A. à une instance de classe. Tous les tableaux de même dimension et de même type ont la même classe et la longueur du tableau n'est pas prise en compte. Le nom correspondant à la Classe est exprimé sous forme de dimension et de type. Par exemple, le nom de classe d'une donnée entière est "[I", le nom de classe d'un tableau d'octets tridimensionnel est "[[[B" et le nom de classe d'une donnée d'objet bidimensionnelle est "[[Ljava. lang.Object".
Les tableaux multidimensionnels sont représentés sous forme de tableaux de tableaux, comme indiqué ci-dessous :
Les tableaux doivent stocker la longueur du tableau, les données du tableau et certaines références aux données de type tableau d'objets dans le tas. Grâce à une référence de tableau, la machine virtuelle doit être capable d'obtenir la longueur d'un tableau, d'accéder à des données spécifiques via l'indexation et d'appeler des méthodes définies par Object. Object est la classe parent directe de toutes les classes de données. Voir le chapitre 6, « Fichiers de classe » pour plus d'informations.
9. Registre PC (compteur de programme) (le compteur de programme)
Un compteur de programme sera créé lorsque chaque thread démarre l'exécution. Le compteur de programme ne comporte qu'un seul mot, il peut donc contenir un pointeur local et renvoyer une valeur. Lorsque le thread s'exécute, le compteur du programme stocke l'adresse de l'instruction en cours d'exécution. Cette adresse peut être un pointeur local ou un pointeur de décalage à partir du bytecode de la méthode. Si une méthode native est exécutée, la valeur du compteur du programme n'est pas définie.
10. La pile Java
Lorsqu'un thread démarre, la machine virtuelle Java crée une pile Java pour celui-ci. La pile Java utilise certaines classes de trames discrètes pour enregistrer l'état des threads. Tas de machine virtuelle Java Il n'y a que deux opérations sur la pile Java : pousser et sauter des images.
La méthode en cours d'exécution dans le thread est appelée la méthode actuelle, et la frame correspondant à la méthode actuelle est appelée la frame actuelle. La classe qui définit la méthode actuelle est appelée la classe actuelle et le pool constant de la classe actuelle est appelé le pool constant actuel. Lorsqu'un thread s'exécute, la machine virtuelle Java garde une trace de la classe actuelle et du pool de constantes actuel. Mais lorsqu’un thread opère sur les données stockées dans la trame, il n’opère que sur les données de la trame actuelle.
Lorsqu'un thread appelle une méthode, la machine virtuelle génère un nouveau frame et le pousse dans la pile Java du thread. Cette nouvelle trame devient la trame actuelle. Lorsque la méthode s'exécute, elle utilise le frame actuel pour enregistrer les paramètres de la méthode, les variables locales, les structures intermédiaires et d'autres données. Les méthodes ont deux manières de sortir : la sortie normale et la sortie anormale. Quelle que soit la manière dont la méthode est lancée, la machine virtuelle Java apparaîtra et supprimera le cadre de la méthode, et le cadre de la méthode précédente deviendra le cadre actuel.
Toutes les données enregistrées dans un cadre ne sont accessibles que par le thread qui en est propriétaire. Les threads ne peuvent pas accéder aux données des piles d'autres threads. Par conséquent, lors de l’accès aux variables locales d’une méthode, il n’est pas nécessaire de considérer la synchronisation multithread.
Comme la zone de méthode et le tas, la pile Java ne nécessite pas d'espace mémoire continu. Elle peut être stockée dans un espace mémoire dispersé ou sur le tas. Les données spécifiques et la longueur de la pile sont définies par l'implémenteur de la machine virtuelle Java. Certaines mises en œuvre peuvent fournir des procédés permettant d'effectuer une maximisation et une minimisation de pile.
11. Le cadre de pile
Le cadre de pile contient trois parties : les variables locales, la pile d'opérandes et les données du cadre. Les tailles des variables locales et des piles d'opérandes sont mesurées en mots et déterminées lors de la compilation. La taille des données de trame dépend de différentes implémentations. Lorsqu'un programme appelle une méthode, la machine virtuelle obtient la taille des variables locales et de la pile d'opérandes à partir des données de classe, crée une taille et un cadre appropriés, puis les place sur la pile Java.
1. Variables locales
Les variables locales sont organisées sous forme de tableau comptant à partir de 0 dans le cadre de pile Java. L'instruction obtient la valeur correspondante de la zone de variable locale en fournissant leur index. Int, float, reference, returnValue occupent un mot, byte, short, char sont convertis en int puis stockés, long et doublel occupent deux mots. La commande
obtient la valeur de long ou double en fournissant le premier des deux indices de mots. Par exemple, si une valeur longue est stockée à l'index 3 ou 4, l'instruction peut obtenir la valeur longue jusqu'à 3.
La zone des variables locales contient les paramètres de méthode et les variables locales. Le compilateur place les paramètres de méthode au début du tableau dans l’ordre dans lequel ils sont déclarés. Cependant, le compilateur peut organiser arbitrairement les variables locales dans le tableau de variables locales, et même deux variables locales peuvent partager une adresse commune, par exemple lorsque deux variables locales se trouvent dans deux zones qui ne se chevauchent pas, comme les variables de boucle i,j.
L'implémenteur de la machine virtuelle peut utiliser n'importe quelle structure pour décrire les données dans la zone de variable locale. La spécification de la machine virtuelle ne définit pas comment stocker long et doublel.
2. Pile d'opérandes (Pile d'opérandes)
Comme les variables locales, la pile d'opérandes est également organisée en tableau en unités de mots. Mais il n'est pas accessible via un index comme les variables locales, mais via des valeurs push et pop. Si une instruction pousse une valeur sur la pile, l'instruction suivante peut apparaître et utiliser cette valeur.
Contrairement au compteur de programme, la pile d'opérandes n'est pas directement accessible par les instructions. Les instructions peuvent accéder directement à la pile d'opérandes. La machine virtuelle Java est basée sur une pile plutôt que sur un registre car ses instructions obtiennent des opérandes de la pile plutôt que du même registre. Bien entendu, les instructions peuvent également obtenir des opérandes provenant d'autres endroits, comme l'opcode après l'instruction ou le pool de constantes. Mais les instructions de machine virtuelle Java obtiennent principalement les opérandes dont elles ont besoin à partir de la pile d'opérandes.
La machine virtuelle Java traite la pile d'opérandes comme une zone de travail. De nombreuses instructions extraient d'abord la valeur de la pile d'opérandes, puis renvoient le résultat vers la pile d'opérandes après le traitement. Le processus d'exécution d'une instruction add est illustré dans la figure ci-dessous : exécutez d'abord les instructions iload_0 et iload_1 pour prendre les deux nombres qui doivent être ajoutés de la zone de méthode locale et les pousser vers la pile d'opérandes, puis exécutez l'instruction iadd ; et maintenant, affichez deux valeurs, ajoutez-les et placez le résultat dans la pile d'opérandes ; exécutez enfin l'instruction istore_2, extrayez le résultat et attribuez-le à la zone de méthode locale.
3. Données de trame
En plus du traitement des variables locales et des piles d'opérandes, les cadres de pile Java incluent également les données nécessaires à la prise en charge des pools constants, des valeurs de retour de méthode et de la distribution des exceptions, et elles sont enregistrées dans les données de trame.
Lorsque la machine virtuelle rencontre une instruction qui utilise une référence au pool constant, elle accédera aux informations requises via le pointeur vers la zone constante dans les données du cadre. Comme mentionné précédemment, les références dans la zone constante sont des références symboliques au début. Même lorsque la machine virtuelle vérifie ces références, ce sont des références de caractères. La machine virtuelle doit donc convertir cette référence à ce moment-là.
Lorsqu'une méthode revient normalement, la machine virtuelle doit reconstruire le frame de pile de la méthode qui a appelé cette méthode. Si la méthode exécutée a une valeur de retour, la machine virtuelle doit pousser cette valeur dans la pile d'opérandes de la méthode appelante.
Les données de trame contiennent également une référence à la table d'exceptions utilisée par la machine virtuelle pour gérer les exceptions. La table des exceptions définit une section de bytecode protégée par une instruction catch. Chaque individu de la table des exceptions contient également la plage de bytecodes qui doivent être protégés et l'emplacement du bytecode qui doit être exécuté lorsque l'exception est interceptée. Lorsqu'une méthode lève une exception, la machine virtuelle Java utilise la table des exceptions pour déterminer comment gérer l'exception. Si la machine virtuelle trouve un catch correspondant, elle transférera le contrôle à l'instruction catch. Si aucun catch correspondant n'est trouvé, la méthode retournera anormalement, puis poursuivra le processus dans la méthode appelante.
En plus des trois utilisations ci-dessus, les données de trame peuvent également contenir des données dépendantes de l'implémentation, telles que des informations de débogage.
12. Pile de méthodes locales
La zone de méthodes locales dépend des différentes implémentations de la machine virtuelle. L'implémenteur de la machine virtuelle peut décider quel mécanisme utiliser pour exécuter les méthodes natives.
Toute interface de méthode native utilise une certaine forme de pile de méthodes natives.
13. Moteur d'exécution
Le cœur de l'implémentation d'une machine virtuelle Java est le moteur d'exécution. Dans la spécification Java Virtual Machine, le moteur d'exécution est décrit comme une séquence d'instructions. Pour chaque directive, la spécification décrit ce qu’elle doit faire, mais pas comment le faire.
1. Jeu d'instructions
Dans la machine virtuelle Java, le flux de bytecode d'une méthode est une séquence d'instructions. Chaque instruction se compose d'un code d'opération d'octet (Opcode) et d'opérandes possibles (Opérandes). L'opcode indique quoi faire et les opérandes fournissent des informations supplémentaires qui peuvent être nécessaires pour exécuter l'opcode. Un moteur d'exécution abstrait exécute une instruction à la fois. Ce processus se produit dans chaque thread d'exécution.
Parfois, le moteur d'exécution peut rencontrer une instruction qui nécessite l'appel d'une méthode locale. Dans ce cas, le moteur d'exécution tentera d'appeler la méthode locale, mais lorsque la méthode locale reviendra, le moteur d'exécution continuera d'exécuter l'étape suivante. le flux de bytecode. Les méthodes natives peuvent également être considérées comme une extension du jeu d’instructions de la machine virtuelle Java.
Décider quelle instruction exécuter ensuite fait également partie du travail du moteur d'exécution. Le moteur d'exécution dispose de trois méthodes pour obtenir l'instruction suivante. La plupart des instructions exécuteront les instructions qui les suivent ; certaines instructions comme goto et return détermineront leur prochaine instruction lorsqu'elles seront exécutées ; lorsqu'une instruction lève une exception, le moteur d'exécution détermine l'instruction suivante en faisant correspondre l'instruction catch qui devrait. être exécuté.
L'indépendance de la plate-forme, la mobilité du réseau et la sécurité influencent la conception du jeu d'instructions de la machine virtuelle Java. L’indépendance de la plate-forme est l’un des principaux facteurs d’influence dans la conception des jeux d’instructions. La structure basée sur la pile permet à la machine virtuelle Java d'être implémentée sur davantage de plates-formes. Des opcodes plus petits et une structure compacte permettent au bytecode d'utiliser la bande passante du réseau plus efficacement. La vérification unique du bytecode rend le bytecode plus sécurisé sans trop affecter les performances.
2. Technologie d'exécution
De nombreuses technologies d'exécution peuvent être utilisées dans l'implémentation de machine virtuelle Java : exécution interprétée, compilation juste à temps, compilation hot-spot, exécution native sur silicium.
3. Threads
La spécification de la machine virtuelle Java définit un modèle de thread pour une implémentation sur davantage de plates-formes. L'un des objectifs du modèle de thread Java est de tirer parti des threads natifs. L'utilisation de threads locaux permet aux threads d'un programme Java d'être exécutés simultanément sur une machine multiprocesseur.
L'un des coûts du modèle de thread Java est la priorité des threads. Un thread Java peut s'exécuter avec un niveau de priorité de 1 à 10. 1 est le plus bas et 10 est le plus élevé. Si le concepteur avait utilisé des threads natifs, il aurait pu mapper ces 10 priorités aux priorités locales. La spécification de la machine virtuelle Java définit uniquement que les threads avec une priorité plus élevée peuvent obtenir du temps CPU, et les threads avec une priorité inférieure peuvent également obtenir du temps CPU lorsque tous les threads de haute priorité sont bloqués, mais il n'y a aucune garantie : priorité faible Le thread ne peut pas obtenir une certaine quantité de temps CPU lorsque le thread hautement prioritaire n'est pas bloqué. Par conséquent, si vous devez coopérer entre différents threads, vous devez utiliser la « synchronisation (synchronizatoin) ».
La synchronisation signifie deux parties : le verrouillage des objets et l'attente et la notification du thread. Les verrous d'objet aident les threads à rester libres de toute interférence d'autres threads. L'attente et l'activation des threads permettent à différents threads de coopérer.
Dans la spécification de la machine virtuelle Java, les threads Java sont décrits comme des variables, de la mémoire principale et de la mémoire de travail. Chaque instance de la machine virtuelle Java possède une mémoire principale qui contient toutes les variables du programme : objets, tableaux et variables de classe. Chaque thread possède sa propre mémoire de travail et conserve des copies des variables qu'il peut utiliser. Règles :
1), copier la valeur de la variable de la mémoire principale vers la mémoire de travail
2), écrire la valeur de la mémoire de travail dans la mémoire principale
Si une variable n'est pas synchronisée, le thread peut mettre à jour la mémoire principale dans n'importe quel ordre variable. Afin de garantir la bonne exécution des programmes multithread, un mécanisme de synchronisation doit être utilisé.
14. Interface de méthode native
L'implémentation de la machine virtuelle Java n'a pas besoin d'implémenter l'interface de méthode native. Certaines implémentations peuvent ne pas prendre en charge les interfaces de méthodes natives. L'interface de méthode native de Sun est JNI (Java Native Interface).
15. La vraie machine
16. Méthode mathématique : Simulation (Eternal Math : A Simulation)
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!