Explication détaillée du tas, de la pile, de la zone de méthode et du chargeur de classe de la machine virtuelle Java (JVM)

1. La structure de base de JVM

diagramme de structure de base jvm
À partir de la figure ci-dessus, nous pouvons voir que la structure de base de JVM comprend: le sous-système de chargement de classe, la zone de mémoire, le moteur d'exécution et l'interface de la bibliothèque locale.

2. Schéma détaillé de chaque zone de JVM

Insérez la description de l'image ici
À travers cette figure, nous aurons une compréhension de base de jvm, expliquons chaque domaine en détail.

3. Explication détaillée de chaque zone de la zone de données pendant l'exécution de la JVM

3.1 Registre du compteur de programme (PC)

L'espace mémoire est petit et le thread est privé. Le travail de l'interpréteur de bytecode est de sélectionner la prochaine instruction de bytecode qui doit être exécutée en modifiant la valeur de ce compteur. Les fonctions de base telles que branchement, boucle, saut, gestion des exceptions et récupération de thread doivent toutes être complétées par le compteur.

Si le thread exécute une méthode Java, ce compteur enregistre l' adresse de l'instruction bytecode de la machine virtuelle en cours d'exécution; s'il exécute une méthode native, la valeur de ce compteur est (Undefined). Cette zone de mémoire est la seule zone qui ne spécifie aucune condition OutOfMemoryError dans la spécification de machine virtuelle Java.

3.2 Piles de machines virtuelles Java

Le thread est privé et le cycle de vie est le même que celui du thread. Il décrit le modèle de mémoire de l'exécution de la méthode Java: chaque méthode crée un cadre de pile lorsqu'elle est exécutée pour stocker des informations telles que la table de variables locales, la pile d'opérandes, le lien dynamique, la sortie de méthode, etc. Chaque méthode de l'appel à la fin de l'exécution correspond au processus d'un frame de pile de la pile de machine virtuelle vers la pile.

Table des variables locales: stocke divers types de base (booléen, byte, char, short, int, float, long, double), les références d'objet (type de référence) et les types returnAddress (pointant vers une instruction bytecode adresse)

StackOverflowError: la profondeur de pile demandée par le thread est supérieure à la profondeur autorisée par la machine virtuelle.
OutOfMemoryError: si la pile de la machine virtuelle peut être développée dynamiquement et qu'une mémoire suffisante ne peut pas être appliquée pendant l'expansion.

3.3 Piles de méthodes natives

La différence avec la pile de machines virtuelles Java est que la pile de machines virtuelles Java sert à la machine virtuelle pour exécuter les méthodes Java (c'est-à-dire le bytecode), tandis que la pile de méthodes locale sert les méthodes natives utilisées par la machine virtuelle. Il y aura également des exceptions StackOverflowError et OutOfMemoryError.

3.4 Mémoire de tas

Pour la plupart des applications, cette zone est la plus grande mémoire gérée par la JVM. Le partage de threads consiste principalement à stocker des instances d'objets et des tableaux. Plusieurs tampons d'allocation privée de thread (Thread Local Allocation Buffer, TLAB) sont divisés en interne. Il peut être situé dans un espace physiquement discontinu, mais logiquement continu.

OutOfMemoryError: s'il n'y a pas de mémoire dans le tas pour terminer l'allocation d'instance et que le tas ne peut plus être développé, cette exception est levée.

3.5 Zone de méthode

Il appartient à la zone de mémoire partagée et stocke des données telles que les informations de classe (y compris le nom de classe, les informations de méthode, les informations de champ), les constantes, les variables statiques et le code compilé par le compilateur JIT qui ont été chargés par la machine virtuelle.

En plus des informations de description des champs, méthodes et interfaces de la classe dans le fichier de classe, il existe également un pool de constantes, qui est utilisé pour stocker les références littérales et symboliques générées lors de la compilation.

Une partie très importante de la zone des méthodes est le pool de constantes d'exécution, qui est la représentation à l'exécution du pool de constantes de chaque classe ou interface. Une fois la classe et l'interface chargées dans la JVM, le pool de constantes d'exécution correspondant est Établi.

Bien sûr, ce n'est pas le contenu du pool de constantes du fichier de classe qui peut entrer dans le pool de constantes d'exécution, et de nouvelles constantes peuvent également être placées dans le pool de constantes d'exécution pendant l'exécution, comme la méthode interne de String. Bien que la spécification JVM décrit la zone de méthode comme une partie logique du tas, elle a l'alias non-tas (non-tas).

Voici une image pour présenter le contenu de la zone de stockage ci-dessus.
Insérez la description de l'image ici

4. Mécanisme de chargement de classe de machine virtuelle

La machine virtuelle charge les données décrivant la classe à partir du fichier de classe dans la mémoire, vérifie les données, analyse et initialise les données, et enfin forme un type Java qui peut être directement utilisé par la machine virtuelle.
Dans le langage Java, le chargement, la connexion et l'initialisation des types sont tous terminés pendant l'exécution du programme.

4.1 Cycle de vie des classes

Insérez la description de l'image ici
La séquence des cinq étapes de chargement, de vérification, de préparation, d'initialisation et de déchargement est déterminée. La phase d'analyse peut démarrer après l'initialisation (liaison d'exécution ou liaison dynamique ou liaison tardive).

Les cinq situations suivantes doivent initialiser la classe (et le chargement, la vérification et la préparation doivent naturellement être terminés avant cela):

  1. Lors de la rencontre des 4 instructions bytecode new, getstatic, putstatic ou invokestatic, l'initialisation n'est pas déclenchée et l'initialisation est déclenchée. Scénarios d'utilisation: utilisez le mot-clé new pour instancier un objet, lisez les champs statiques d'une classe (à l'exception des champs statiques qui ont été modifiés par final et placez les résultats dans le pool de constantes au moment de la compilation) et appelez les méthodes statiques d'une classe.
  2. Lorsque vous utilisez la méthode de package java.lang.reflect pour effectuer un appel de réflexion vers la classe.
  3. Lors de l'initialisation d'une classe, s'il s'avère que sa classe parente n'a pas encore été initialisée, l'initialisation de sa classe parente doit d'abord être déclenchée.
  4. Lorsque la machine virtuelle démarre, l'utilisateur doit spécifier une classe principale à charger (la classe contenant la méthode main ()), et la machine virtuelle initialise la classe principale en premier.
  5. Lors de l'utilisation de la prise en charge du langage dynamique de JDK 1.7, si les descripteurs de méthode de REF_getStatic, REF_putStatic et REF_invokeStatic sont les résultats de l'analyse finale d'une instance java.lang.invoke.MethodHandle, et que la classe correspondant à ce descripteur de méthode n'a pas été initialisée, elle doit d'abord être déclenchée Son initialisation.

4.2 Processus de chargement de classe

4.2.1 Chargement

  1. Obtenez le flux binaire (package ZIP, réseau, génération d'opérations, génération JSP, lecture de base de données) qui définit la sous-classe via le nom complet d'une classe.
  2. La structure de stockage statique représentée par ce flux d'octets est transformée en structure de données d'exécution de la zone de méthode.
  3. Un objet java.lang.Class représentant cette classe est généré en mémoire comme méthode pour accéder à diverses données de cette classe.

La particularité de la classe tableau: la classe tableau elle-même n'est pas créée par le chargeur de classe, elle est créée directement par la machine virtuelle Java. Cependant, la classe de tableau et le chargeur de classe ont toujours une relation étroite, car le type d'élément de la classe de tableau est finalement créé par le chargeur de classe. Le processus de création de tableau est le suivant:

  1. Si le type de composant du tableau est un type référence, utilisez le chargement de classe de manière récursive.
  2. Si le type de composant du tableau n'est pas un type de référence, la machine virtuelle Java marquera le tableau comme une association de chargeur de classe d'amorçage.
  3. La visibilité de la classe de tableau est cohérente avec la visibilité de son type de composant. Si le type de composant n'est pas un type de référence, la visibilité de la classe de tableau sera par défaut publique.

L'objet java.lang.Class de l'instance en mémoire est stocké dans la zone de méthode. En tant qu'interface externe pour ces types de données dans la zone de méthode d'accès au programme. Une partie du contenu de la phase de chargement et de la phase de connexion est entrelacée, mais l'heure de démarrage reste en ordre.

4.2.2 Vérification

Il s'agit de la première étape de connexion pour garantir que les informations contenues dans le flux d'octets du fichier de classe répondent aux exigences de la machine virtuelle actuelle.

Vérification du format de fichier

  1. Commencer par le nombre magique 0xCAFEBABE
  2. Si les numéros de version majeurs et mineurs se trouvent dans la plage de traitement de la machine virtuelle actuelle
  3. Si les constantes du pool de constantes ont des types de constantes non pris en charge (vérifiez l'indicateur de balise constante)
  4. Y a-t-il une constante qui pointe vers une constante inexistante ou une constante qui n'est pas conforme au type parmi les différentes valeurs d'index qui pointent vers la constante
  5. Y a-t-il des données non conformes au codage UTF8 dans la constante de type CONSTANT_Utf8_info
  6. Si chaque fichier d'ensemble de pièces lui-même dans le fichier de classe a d'autres informations supplémentaires supprimées
  7. ……

Ce n'est qu'après avoir passé cette étape de vérification que le flux d'octets entrera dans la zone de méthode de la mémoire pour le stockage, de sorte que les trois étapes de vérification suivantes sont toutes basées sur la structure de stockage de la zone de méthode, et le flux d'octets n'est plus directement manipulé.

Vérification des métadonnées

  1. Cette classe a-t-elle une classe parente (sauf java.lang.Object)
  2. Si la classe parente de cette classe hérite d'une classe dont l'héritage n'est pas autorisé (classe modifiée finale)
  3. Si cette classe n'est pas une classe abstraite, a-t-elle implémenté toutes les méthodes requises pour être implémentées dans sa classe ou interface parente
  4. Si les champs et les méthodes de la classe sont en conflit avec la classe parente (remplacent le champ final de la classe parente et surcharge qui n'est pas conforme à la spécification)

Cette étape consiste principalement à effectuer une vérification sémantique sur les informations de métadonnées de la classe pour s'assurer qu'il n'y a pas d'informations de métadonnées non conformes à la spécification du langage Java.

Vérification du bytecode

  1. Assurez-vous que le type de données de la pile d'opérandes et la séquence de codes d'instructions fonctionnent ensemble à tout moment (la lecture d'une donnée de type int selon le type long ne se produira pas)
  2. Assurez-vous que les instructions de saut ne passeront pas aux instructions de bytecode en dehors du corps de la méthode
  3. Assurez-vous que la conversion de type dans le corps de la méthode est valide (il est sûr d'attribuer des objets de sous-classe au type de données parent, et vice versa est illégale)
  4. ……

Il s'agit de l'étape la plus compliquée de tout le processus de vérification, l'objectif principal étant de déterminer que la sémantique du programme est légale et logique grâce à l'analyse des flux de données et des flux de contrôle. À ce stade, le corps de méthode de la classe est vérifié et analysé pour s'assurer que la méthode de la classe de vérification ne provoquera pas d'événement mettant en danger la sécurité de la machine virtuelle pendant le fonctionnement.

Vérification de la référence des symboles

  1. Si le nom complet décrit par la chaîne dans la référence de symbole peut trouver la classe correspondante
  2. S'il existe un descripteur de champ de la méthode de symbole et la méthode et le champ décrits par le nom simple dans la classe spécifiée
  3. Si l'accessibilité (privée, protégée, publique, par défaut) de la classe, du champ et de la méthode dans la référence de symbole est accessible par la classe actuelle
  4. ...... La
    dernière étape de vérification se produit lorsque la référence de symbole est rapidement convertie en référence directe Cette action de conversion se produira dans la troisième étape de la connexion, la phase d'analyse. La vérification de référence de symbole peut être considérée comme une vérification de correspondance d'informations autres que la classe elle-même (diverses références de symboles dans le pool de constantes), ainsi que du contenu mentionné ci-dessus.
    Le but de la référence de symbole est de garantir que l'action d'analyse peut être exécutée normalement. Si la vérification de la référence de symbole échoue, une sous-classe de java.lang.IncompatibleClass.ChangeError sera générée. Tels que java.lang.IllegalAccessError, java.lang.NoSuchFieldError, java.lang.NoSuchMethodError, etc.

4.2.3 Préparation

Cette étape alloue formellement de la mémoire pour la classe et définit la valeur initiale de la variable de classe, et la mémoire est allouée dans la méthode (les variables avec modification statique n'incluent pas les variables d'instance).

valeur int statique publique = 1127;

Ce code est 0 après la définition de la valeur initiale, car aucune méthode Java n'a encore été exécutée pour le moment. L'instruction putstatic qui attribue la valeur à 1127 est stockée dans la méthode clinit () après la compilation du programme, de sorte que la valeur est attribuée pendant la phase d'initialisation.

La valeur zéro du type de données de base

Cas particulier: Si l'attribut ConstantValue existe dans la table attributaire de champ du champ de classe, la machine virtuelle attribuera la valeur à 1127 selon le paramètre ConstantValue pendant la phase de préparation.

4.2.4 Analyse

À ce stade, la machine virtuelle remplace les références de symboles dans le pool de constantes par des références directes.

  1. Référence de symbole La référence de
    symbole est un ensemble de symboles pour décrire la cible référencée, et le symbole peut être n'importe quelle forme de littéral.
  2. Référence
    directe La référence directe peut être un pointeur vers la cible, un décalage relatif ou une poignée qui peut localiser indirectement la cible. La référence directe est liée à l'implémentation de la configuration mémoire de Xunji

L'action d'analyse est principalement effectuée pour 7 types de références de symboles de classes ou d'interfaces, de champs, de méthodes de classe, de méthodes d'interface, de types de méthode, de descripteurs de méthode et de qualificatifs de point d'appel, qui correspondent aux 7 types de constantes du pool de constantes.

4.2.5 Initialisation

Le processus précédent est dominé par la machine virtuelle et la phase d'initialisation commence à exécuter le code Java dans la classe.

Chargeur de classe 4.3

Obtenez un flux d'octets binaire décrivant cette classe via le nom complet d'une classe.

4.3.1 Modèle de délégation des parents

Du point de vue de la machine virtuelle Java, il n'y a que deux chargeurs de classe: l'un est le chargeur de classe de démarrage (implémenté en C ++, qui fait partie de la machine virtuelle); l'autre est le chargeur pour toutes les autres classes (implémenté en Java, indépendamment de la machine virtuelle) Externement et entièrement hérité de java.lang.ClassLoader)

1. Démarrez le chargeur de classe et
chargez les classes sous lib ou sous -Xbootclasspath

2. Le chargeur de classe étendu
charge lib / ext ou les classes sous le chemin spécifié par la variable système java.ext.dirs

3. Le chargeur de classe du programme de référence
ClassLoader est responsable du chargement de la bibliothèque de classes spécifiée sur le chemin utilisateur.

Insérez la description de l'image ici
À l'exception du chargeur de classe de démarrage de niveau supérieur, tous les autres ont leur propre chargeur de classe parent.
Processus de travail : lorsqu'un certain chargeur de classe reçoit une demande de chargement d'une classe, il délègue d'abord la tâche de chargement au chargeur de classe parent, puis de manière récursive. Si le chargeur de classe parent peut terminer la tâche de chargement de classe, il renvoie correctement; si la classe parent se charge Si l'appareil ne peut pas terminer la tâche de chargement, il lèvera une exception ClassNotFoundException, puis appellera sa propre méthode findClass () pour charger, et ainsi de suite.

4.3.2 Le rôle du modèle de délégation parentale

1. Assurez-vous que les classes principales fournies par JVM ne sont pas altérées et assurez la sécurité de l'exécution des classes

Par exemple, la classe de chaîne ci-dessus, quel que soit le chargeur qui souhaite charger cette classe, en raison du mécanisme de délégation parent, elle sera éventuellement chargée par le chargeur de classe de démarrage de niveau supérieur, ce qui garantit que la classe de chaîne se trouve dans divers environnements de chargeur de classe. Ils sont tous de la même classe. Imaginez que s'il n'y a pas de mécanisme de délégation parentale, chaque chargeur charge la classe de chaîne par lui-même. Il est possible que les méthodes de chaîne chargées par différents chargeurs de classe soient différentes. Dans ce cas, notre programme sera chaotique.

2. Empêchez le chargement répété de la même classe

Je suppose que tu aimes

Origine blog.csdn.net/cyb_123/article/details/108513384
conseillé
Classement