1 aperçu
Java est connu pour prendre en charge la plate-forme agnostique, la sécurité et la mobilité du réseau. La plate-forme Java est composée de machines virtuelles Java et de classes Java Core, qui fournit une interface de programmation unifiée pour les programmes Java purs, quel que soit le système d'exploitation inférieur. C'est précisément à cause de la machine virtuelle Java que sa prétention à être "compilée une fois, exécuté partout" peut être garantie.
1.1 Processus d'exécution du programme Java
L'exécution des programmes Java dépend de l'environnement de compilation et de l'environnement de course. Le code source est converti en code machine exécutable, qui est terminé par le processus suivant:
Le cœur de la technologie Java est la machine virtuelle Java, car tous les programmes Java s'exécutent sur la machine virtuelle. Le fonctionnement des programmes Java nécessite la coopération des fichiers Java Virtual Machine, Java API et Java. L'instance Java Virtual Machine est responsable de l'exécution d'un programme Java. Lorsqu'un programme Java est démarré, une instance de machine virtuelle est née. Lorsque le programme se termine, l'instance de machine virtuelle décède.
La fonctionnalité multiplateforme de Java est car elle a des machines virtuelles ciblant différentes plates-formes.
1.2 Machine virtuelle Java
La tâche principale d'une machine virtuelle Java est de charger des fichiers de classe et d'exécuter les bytecodes qui s'y trouvent. Comme le montre la figure ci-dessous, la machine virtuelle Java contient un chargeur de classe, qui peut charger des fichiers de classe à partir de programmes et d'API. Seules les classes requises pour l'exécution du programme seront chargées dans l'API Java, et le bytecode est exécuté par le moteur d'exécution.
Lorsqu'une machine virtuelle Java est implémentée par un logiciel sur le système d'exploitation de l'hôte, le programme Java interagit avec l'hôte en appelant des méthodes locales. Les méthodes Java sont écrites en langue java, compilées en bytecode et stockées dans des fichiers de classe. La méthode locale est écrite en langage C / C ++ / Assembly, compilé en code machine lié au processeur, stocké dans la bibliothèque de liens dynamiques, et le format est propriétaire de chaque plate-forme. La méthode locale consiste donc à connecter les programmes Java au système d'exploitation hôte sous-jacent.
Étant donné que la machine virtuelle Java ne sait pas comment un fichier de classe a été créé et s'il a été falsifié, il implémente un détecteur de fichiers de classe pour s'assurer que les types définis dans le fichier de classe peuvent être utilisés en toute sécurité. Le vérificateur de fichiers de classe assure la robustesse du programme par le biais de quatre analyses indépendantes:
・ Collection de fichiers de classe
・ Vérification sémantique des données de type
・ Vérification du bytecode
・ Vérification de référence du symbole
Lors de l'exécution de ByteCode, les machines virtuelles Java effectuent également d'autres mécanismes de sécurité intégrés. Ce sont les caractéristiques de l'assurance de la robustesse des programmes Java en tant que langages de programmation Java, et sont également les caractéristiques des machines virtuelles Java:
・ Conversion de référence de type type
・ Accès à la mémoire structurée
・ Collection automatique des ordures
・ Vérification des limites du tableau
・ Vérification vide de la citation
1.3 Type de données de la machine virtuelle Java
Les machines virtuelles Java effectuent des calculs via certains types de données. Les types de données peuvent être divisés en deux types: types de base et types de référence, comme indiqué dans la figure ci-dessous:
Mais Boolean est un peu spécial. Lorsque le compilateur compile le code source Java en bytecode, il représentera booléen avec int ou octet. Dans Java Virtual Machines, False est représenté par 0, et True est représenté par tous les entiers non nuls. Comme le langage Java, la plage de valeur du type de base d'une machine virtuelle Java est cohérente partout, quelle que soit la plate-forme hôte, un long est toujours un entier signé avec le complément de 64 bits deux dans n'importe quelle machine virtuelle.
Pour ReturnAddress, ce type de base est utilisé pour implémenter la clause enfin dans un programme Java. Les programmeurs Java ne peuvent pas utiliser ce type, sa valeur pointe vers l'opcode d'une instruction de machine virtuelle.
2Architecture
Dans la spécification de la machine virtuelle Java, le comportement d'une instance de machine virtuelle est décrit en termes de sous-système, de zone de mémoire, de type de données et d'instructions, et ces composants montrent ensemble l'architecture interne de machine virtuelle virtuelle abstraite.
Fichier 2.1 CLASS
Le fichier Javaclass contient toutes les informations sur une classe ou une interface. Le "type de base" du fichier de classe est le suivant:
| U1 | 1 octet, type non signé |
| U2 | 2 octets, type non signé |
| U4 | 4 octets, type non signé |
| U8 | 8 octets, type non signé |
Si vous voulez en savoir plus, JVM SE7 d'Oracle donne la spécification officielle: la spécification de la machine virtuelle Java®
Le contenu du fichier de classe:
ClassFile {U4 Magic; // Numéro magique: 0xcafebabe, utilisé pour déterminer s'il s'agit d'un fichier de classe Java U2 Minor_version; // Numéro de version mineure U2 Major_version; // Numéro de version principale U2 constant_pool_count; // Taille du pool constant cp_info constant_pool [constant_pool_count-1]; // Pool constant U2 Access_Flags; // Accédez aux indicateurs à des niveaux de classe et d'interface (obtenus via | opération) U2 This_class; // Index de classe (pointant vers les constantes de classe dans Pool constant) U2 Super_class; // Index de classe actuel (pointant vers les constantes de classe dans un pool constant) u2 interfaces_count; // Interfaces Index Counter U2 Interfaces [interfaces_count]; // Index d'interface set u2 fields_count; // Count Count Counter Field_info Fields [Fields_Count]; // Table de champ set u2 Methods_Count; // Méthode Count Counter Method_Info Méthodes [Methods_Count]; // TABLE MÉTHODE SET U2 Attributes_Count; // Nombre d'attributs attribut_info attributs [attributs_count]; // Tableau d'attribut} 2.2 Sous-système de chargeur de classe
Le sous-système de chargeur de classe est responsable de la recherche et du chargement des informations de type. En fait, il existe deux types de chargeurs pour les machines virtuelles Java: les chargeurs système et les chargeurs définis par l'utilisateur. Le premier fait partie de l'implémentation de la machine virtuelle Java, tandis que le second fait partie du programme Java.
・ BootstrapClassLoader: Il est utilisé pour charger la bibliothèque de base de Java, implémentée dans le code natif, et n'est pas hérité de java.lang.classloader.
・ ExtensionClassLoader: Il est utilisé pour charger les bibliothèques d'extension Java. L'implémentation de la machine virtuelle Java fournira un répertoire de bibliothèque d'extension. Ce chargeur de classe recherche et charge les classes Java dans ce répertoire.
・ Chargeur de classe d'application: il charge les classes Java en fonction de l'application ClassPath of Java (CLASSPATH). D'une manière générale, les classes d'application Java en sont chargées. Il peut être obtenu via classloader.getSystemClassloader ().
En plus des chargeurs de classe fournis par le système, les développeurs peuvent implémenter leurs propres chargeurs de classe en héritant de la classe java.lang.classloader pour répondre à certains besoins spéciaux.
Le sous-système de chargeur de classe implique plusieurs autres composants de la machine virtuelle Java et des classes de la bibliothèque java.lang. La méthode définie par Classloader fournit une interface pour le programme pour accéder au mécanisme de chargeur de classe. De plus, pour chaque type chargé, la machine virtuelle Java crée une instance de la classe java.lang.class pour représenter le type. Comme d'autres objets, les chargeurs de classe définis par l'utilisateur et les instances de classe sont placés dans la zone du tas en mémoire, tandis que les informations de type chargé se trouvent dans la zone de la méthode.
En plus de localiser et d'importer des fichiers de classe binaire, le sous-système de chargeur de classe doit également être responsable de la vérification de l'exactitude de la classe importée, de l'allocation et de l'initialisation de la mémoire pour les variables de classe et de l'analyse des références symboliques. Ces actions doivent également être effectuées dans l'ordre suivant:
・ Charge (trouver et charger des données binaires de type)
・ Connexion (vérification de l'exécution: assurez-vous l'exactitude du type importé; Préparation: allouer la mémoire aux variables de classe et les initialiser aux valeurs par défaut; analyse: convertir les références symboliques dans le type en références directes)
・ Initialisation (les variables de classe sont initialisées à la valeur initiale correcte)
2.3 Zone de méthode
Dans une machine virtuelle Java, des informations sur le type chargé sont stockées en mémoire dans une zone de méthode. Lorsqu'une machine virtuelle charge un certain type, il utilise un chargeur de classe pour localiser le fichier de classe correspondant, puis lit le fichier de classe et le transfère à la machine virtuelle. Ensuite, la machine virtuelle extrait les informations de type et stocke ces informations dans la zone de méthode. Les zones de méthode peuvent également être collectées par le collecteur des ordures, car la machine virtuelle permet une extension dynamique des programmes Java via des chargeurs de classe définis par l'utilisateur.
Les informations suivantes sont stockées dans la zone de la méthode:
・ Ce type de nom entièrement qualifié (comme le nom entièrement qualifié java.lang.object)
・ Le nom entièrement qualifié de ce type de superclasse directe
・ Ce type de classe ou le type d'interface est-il
・ Ce type de modificateur d'accès (un sous-ensemble de public, abstrait, final)
・ Liste triée des noms entièrement qualifiés pour n'importe quel hyperinterface directe
・ Pool constant de ce type (une collection ordonnée comprenant des constantes directes [cordes, entières et constantes de point flottantes] et des références symboliques à d'autres types, champs et méthodes)
・ Informations sur le terrain (nom de champ, type, modificateur)
・ Informations sur la méthode (nom de la méthode, type de retour, nombre de paramètres et type, modificateur)
・ Toutes les variables de classe (statique) sauf les constantes
・ Référence à la classe Classloader (Lorsque chaque type est chargé, la machine virtuelle doit suivre si elle est chargée par le chargeur de classe de démarrage ou le chargeur de classe définie par l'utilisateur)
・ Référence à la classe de classe (pour chaque type chargé, la machine virtuelle créera une instance de la classe java.lang.class en conséquence. Par exemple, si vous avez une référence à l'objet de la classe java.lang.integer, alors vous n'avez qu'à appeler la méthode getClass () référencée par l'objet Integer pour obtenir l'objet de classe représentant la classe Java.lang.ing.
2,4 tas
Toutes les instances de classe ou les tableaux créés par les programmes Java à l'exécution (les tableaux sont un véritable objet dans une machine virtuelle Java) sont placés dans le même tas. Étant donné que les instances de machine virtuelle Java n'ont qu'un seul espace de tas, tous les threads partageront ce tas. Il convient de noter que la machine virtuelle Java a une instruction pour allouer des objets dans le tas, mais n'a pas d'instructions pour libérer de la mémoire, car la machine virtuelle a remis cette tâche au collecteur des ordures pour le traitement. La spécification de la machine virtuelle Java n'applique pas les collectionneurs de déchets, il nécessite seulement que les implémentations de la machine virtuelle doivent gérer leur propre espace de tas "d'une manière ou d'une autre". Par exemple, une implémentation ne peut avoir qu'un espace de tas de taille fixe. Lorsque l'espace est rempli, il lance simplement une exception d'ouvre, qui ne tient pas compte de la question du recyclage des objets à ordures, mais il est conforme aux spécifications.
La spécification de la machine virtuelle Java ne spécifie pas comment les objets Java sont représentés dans le tas, ce qui donne à l'implémenteur des décisions de machine virtuelle sur la façon de concevoir. Une conception possible de tas est la suivante:
Une piscine de poignée, une piscine d'objet. La référence d'un objet est un pointeur local vers le pool de poignées. Les avantages de cette conception sont propices au tri des fragments de tas. Lorsque vous déplacez des objets dans le pool d'objets, la pièce de manche doit uniquement modifier la nouvelle adresse du pointeur pointant vers l'objet. L'inconvénient est que chaque fois qu'une variable d'instance d'un objet est accessible, elle doit être transmise par deux pointeurs.
2.5 Java Stack
Chaque fois qu'un thread est démarré, la machine virtuelle Java y alloue une pile Java. Une pile Java se compose de nombreuses cadres de pile, un cadre de pile contient l'état d'un appel de méthode Java. Lorsqu'un thread appelle une méthode Java, la machine virtuelle pousse une nouvelle trame de pile dans la pile Java du thread. Lorsque la méthode revient, le cadre de pile apparaît à partir de la pile Java. La pile Java stocke l'état des appels de la méthode Java dans les threads - y compris les variables locales, les paramètres, les valeurs de retour et les résultats d'intermédiaire, etc. Les machines virtuelles Java n'ont pas de registres, et leur ensemble d'instructions utilise une pile Java pour stocker des données intermédiaires. La raison de cette conception est de garder le jeu d'instructions de la machine virtuelle Java aussi compacte que possible, et de faciliter également la mise en œuvre de la machine virtuelle Java sur une plate-forme avec peu de registres généraux. De plus, l'architecture basée sur la pile aide également à optimiser le code des compilateurs dynamiques et des compilateurs instantanés implémentés par certaines machines virtuelles pendant l'exécution.
2.5.1 Cadre de pile
Un cadre de pile se compose d'une zone variable locale, d'une pile d'opérande et d'une zone de données de trame. Lorsqu'une machine virtuelle appelle une méthode Java, elle obtient la zone de variable locale et la taille de la pile de l'opérande de cette méthode à partir des informations de type de la classe correspondante, et alloue la mémoire du cadre de pile en fonction de cela, puis la pousse dans la pile Java.
2.5.1.1 zone variable locale
La zone variable locale est organisée en un tableau compté à partir de 0 en unités de longueur de mot. L'instruction bytecode utilise les données dedans via un index à partir de 0. Valeurs de types int, float, référence et returnAddress occupent un élément dans le tableau, tandis que les valeurs des types octets, courts et char sont converties en valeurs int avant d'être stockées dans le tableau et occupent également un élément. Mais les valeurs des types longs et doubles occupent deux termes consécutifs dans le tableau.
2.5.1.2 Stack d'opérande
Comme la zone variable locale, la pile d'opérande est également organisée en un tableau en longueur de mot. Il accède via Standard Stack Operations Stack et Stack Out. Étant donné que le compteur du programme ne peut pas être directement accessible par les instructions du programme, les instructions de la machine virtuelle Java obtiennent des opérandes à partir de la pile d'opérands, de sorte que son fonctionnement est basé sur la pile plutôt que sur les registres. La machine virtuelle prend la pile d'opérande comme espace de travail, car la plupart des instructions doivent faire apparaître des données à partir d'ici, effectuer des opérations, puis repousser le résultat vers la pile de l'opérande.
2.5.1.3 Zone de données de trame
En plus de la zone variable locale et de la pile d'opérande, les cadres de pile Java ont également besoin de zones de données de trame pour prendre en charge l'analyse constante du pool, le rendement normal de la méthode et les mécanismes d'exception de répartition. Chaque fois qu'une machine virtuelle souhaite exécuter une instruction qui nécessite des données de pool constantes, il y accède via un pointeur au pool constant dans la zone de données de trame. En plus de l'analyse des pools constants, la zone de données de trame aide également la machine virtuelle à gérer l'extrémité normale ou l'abandon anormale des méthodes Java. Si le retour se termine normalement, la machine virtuelle doit restaurer la trame de pile de la méthode initiant l'appel, y compris la définition du compteur de programme pour pointer vers l'instruction suivante initiant la méthode d'appel; Si la méthode a une valeur de retour, la machine virtuelle doit la pousser dans la pile d'opérande de la méthode initiant l'appel. Pour gérer l'exception des sorties lors de l'exécution de la méthode Java, la zone de données de trame détient également une référence au tableau d'exception de cette méthode.
2.6 compteur de programme
Pour un programme Java en cours d'exécution, chaque fil a son compteur de programme. Les compteurs de programme sont également appelés registres PC. Le compteur du programme peut contenir à la fois un pointeur local et un retour d'adhésion. Lorsqu'un thread exécute une méthode Java, la valeur du compteur de programme est toujours l'adresse de l'instruction exécutée suivante. L'adresse ici peut être un pointeur local ou un décalage dans la méthode bytecode par rapport à l'instruction de démarrage de la méthode. Si le thread exécute une méthode locale, la valeur du compteur du programme est "non définie".
2.7 pile de méthode locale
Toute interface de méthode locale utilisera une sorte de pile de méthode locale. Lorsqu'un thread appelle une méthode Java, la machine virtuelle crée une nouvelle trame de pile et la pousse dans la pile Java. Lorsqu'il appelle une méthode locale, la machine virtuelle maintient la pile Java inchangée et ne pousse plus dans la nouvelle pile dans la pile Java filetée. La machine virtuelle se connecte simplement dynamiquement et appelle directement la méthode locale spécifiée.
La zone de méthode et le tas sont partagés par tous les threads de l'instance de machine virtuelle. Lorsque la machine virtuelle charge un fichier de classe, il analyse les informations de type des données binaires contenues dans le fichier de classe, puis place les informations de type dans la zone de la méthode. Lorsque le programme est en cours d'exécution, la machine virtuelle place tous les objets créés par le programme lors de l'exécution dans le tas.
Comme les autres zones de mémoire d'exécution, la zone de mémoire occupée par la pile de méthode locale peut être élargie ou rétrécie dynamiquement au besoin.
3 moteur d'exécution
Dans la spécification de la machine virtuelle Java, le comportement du moteur d'exécution est défini à l'aide d'ensembles d'instructions. Le concepteur implémentant le moteur d'exécution décidera comment exécuter des bytecode, l'implémentation peut être interprétée, compilée à la volée ou exécutée directement à l'aide d'instructions sur la puce, ou un mélange d'entre elles.
Le moteur d'exécution peut être compris comme une spécification abstraite, une implémentation concrète ou une instance en cours. Les spécifications abstraites utilisent des ensembles d'instructions pour spécifier le comportement du moteur d'exécution. Une implémentation spécifique peut utiliser une variété de technologies différentes - y compris des logiciels, du matériel ou une combinaison de technologie d'arbres. Le moteur d'exécution en tant qu'instance d'exécution est un thread.
Chaque thread d'un programme Java en cours d'exécution est une instance d'un moteur d'exécution de machine virtuelle indépendante. Du début à la fin du cycle de vie du thread, il exécute Bytecode ou exécute une méthode locale.
3.1 Ensemble d'instructions
Le flux bytecode de la méthode est composé d'une séquence d'instructions d'une machine virtuelle Java. Chaque instruction contient un OPCode à un octet suivi de 0 opérandes ou plus. L'opcode représente l'opération à effectuer; L'opérand fournit à la machine virtuelle Java des informations supplémentaires nécessaires pour exécuter l'opcode. Lorsqu'une machine virtuelle exécute une instruction, elle peut utiliser les éléments dans le pool constant actuel, les valeurs de la variable locale du cadre actuel ou les valeurs en haut de la pile d'opérande du cadre actuel.
Le moteur d'exécution abstrait exécute une instruction bytecode à la fois. Chaque thread (instance d'exécution du moteur) d'un programme exécuté dans une machine virtuelle Java effectue cette opération. Le moteur d'exécution obtient l'opcode, et si l'opcode a un opérande, il obtient son opérande. Il effectue l'action spécifiée par l'opcode et l'opérande suivant, puis obtient l'opcode suivant. Ce processus d'exécution de bytecode se poursuivra jusqu'à la fin du thread, et l'achèvement du thread peut être marqué par le retour à partir de sa méthode initiale ou ne capture pas l'exception lancée.
4 Interface de méthode locale
L'interface locale Java, également appelée JNI (JavanativeInterface), est préparée pour la portabilité. L'interface de la méthode locale permet à la méthode locale de procéder ce qui suit:
Passer ou retourner des données
Variables d'instance de fonctionnement
Faire fonctionner des variables de classe ou des méthodes de classe d'appel
Tableau d'opérande
Verrouiller l'objet tas
Chargez une nouvelle classe
faire une exception
Attrapez l'exception lancée par une méthode locale appelant une méthode Java
Capturez une exception asynchrone lancée par la machine virtuelle
Indique qu'un objet collecteur de déchets n'est plus nécessaire
Résumer
Ce qui précède concerne cet article sur une compréhension approfondie de l'architecture de la machine virtuelle Java, et j'espère qu'elle sera utile à tout le monde. Les amis intéressés peuvent continuer à se référer à d'autres sujets connexes sur ce site. S'il y a des lacunes, veuillez laisser un message pour le signaler. Merci vos amis pour votre soutien pour ce site!