Cet article vous apporte une introduction détaillée (exemple de code) sur le mécanisme de chargement des classes JVM. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.
Java est différent des langages compilés traditionnels tels que C/C++, et également différent des langages de script dynamiques tels que PHP. On peut dire que Java est un langage semi-compilé. La classe que nous écrivons sera d'abord compilée dans un fichier .class. Ce .class est un flux d'octets binaires. Ensuite lorsque cette classe sera utilisée, le fichier .class correspondant à cette classe sera chargé en mémoire. Le contenu de ce .class est chargé en mémoire via le mécanisme de chargement de classe Jvm.
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, les convertit, les analyse et les initialise, et forme enfin un type Java qui peut être utilisé directement par la machine virtuelle. Il s'agit de la classe du mécanisme de chargement de la machine virtuelle.
Chargement
La première étape du processus de "chargement de classe" lors du chargement , Pendant le processus de chargement, la machine virtuelle doit effectuer les trois choses suivantes :
Obtenez le flux d'octets binaires qui définit cette classe via le nom complet d'une classe.
Convertissez la structure de stockage statique représentée par ce flux d'octets en une structure de données d'exécution dans la zone de méthode.
Générer un objet java.lang.Class représentant cette classe dans la mémoire, qui sert d'entrée d'accès à diverses données de cette classe dans la zone méthode.
Il convient de mentionner que la phase de chargement peut être complétée à l'aide du chargeur de classe de démarrage fourni par le système ou par un chargeur de classe défini par l'utilisateur, mais. ce n'est pas le cas pour les tableaux. La classe tableau elle-même n'est pas créée par chargement de classe, elle est créée directement par la machine virtuelle Java. Mais le type d'élément dans lequel les données sont stockées doit être créé par le chargeur de classe.
La phase de chargement et la partie de connexion de la phase suivante sont entrelacées, mais l'heure de début de la phase de chargement et la phase de connexion conserveront toujours une séquence fixe.
Vérification
La vérification est la première étape de la phase de connexion. Le but de cette phase est de s'assurer que les informations contenues dans le flux d'octets du fichier Classe sont. combiné aux exigences actuelles de la machine virtuelle et ne mettra pas en danger la sécurité de la machine virtuelle elle-même. Bien que les opérations telles que les tableaux hors limites et la transformation aléatoire d'objets soient rejetées par le compilateur, le fichier .class n'a pas nécessairement besoin d'être compilé à partir du code source Java et peut donc être généré par d'autres moyens. du fichier .class doit être vérifié.
L'importance de la phase de vérification va de soi. Que cette phase soit rigoureuse ou non détermine directement si la machine virtuelle Java peut résister aux attaques de code malveillant. Du point de vue des performances d'exécution, le travail de la phase de vérification. La charge représente une part considérable du système de chargement de classes de la machine virtuelle.
Dans l'ensemble, la phase de vérification peut être grossièrement divisée en 4 parties d'actions de vérification : vérification du format de fichier, vérification des métadonnées, vérification du bytecode et vérification de la référence du symbole.
Vérification des symboles : l'objectif principal est de garantir que le flux d'octets d'entrée peut être correctement analysé et stocké dans la zone de méthode, et que le format répond aux exigences de description d'informations de type Java. . Cette partie est basée sur la vérification du flux binaire, qui sera ensuite chargé en mémoire, et une vérification ultérieure est effectuée en mémoire.
Vérification des métadonnées : Cette vérification effectue principalement une vérification sémantique sur les informations de métadonnées de la classe pour garantir qu'il n'y a aucune information de métadonnées non conforme à la spécification du langage Java.
Vérification du bytecode : Cette partie est la phase la plus complexe de la phase de vérification. L'objectif principal est de déterminer que le programme est légal et logique grâce à l'analyse du flux de données et du flux de contrôle.
Vérification de la référence du symbole : la référence du symbole se produit lorsque la machine virtuelle convertit la référence du symbole en une référence directe, afin que l'action d'analyse puisse être exécutée normalement.
Préparation
La phase de préparation est la phase où la mémoire est allouée aux variables de classe formelles (variables statiques) et aux valeurs initiales de les variables de classe sont définies. La mémoire utilisée est allouée dans la zone de méthode. Il convient de mentionner que seules les variables de classe (variables statiques) sont allouées à ce stade, pas les variables d'instance.
Habituellement, définissez la valeur initiale d'une variable de classe. Cette valeur initiale fait référence à la valeur par défaut du type de données, telle que 0 pour le type int. Mais si la variable de classe est modifiée par final, la situation est différente. Dans ce cas, la valeur donnée sera affectée directement.
Analyse
La phase d'analyse est le processus dans lequel la machine virtuelle remplace les références de symboles dans le pool constant par des références directes. Voici une explication de ce qu’est une référence symbolique et de ce qu’est une référence directe.
Référence symbolique : une référence symbolique utilise un ensemble de symboles pour décrire la cible référencée. Le symbole peut être n'importe quelle forme de littéral, à condition que la cible puisse être localisée sans ambiguïté lorsqu'elle est utilisée.
Référence directe : Une 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.
L'action d'analyse est principalement effectuée sur 7 types de références de symboles : classes ou interfaces, champs, méthodes de classe, méthodes d'interface, types de méthodes, descripteurs de méthode et qualificatifs de site d'appel.
Initialisation
La phase d'initialisation de la classe est la dernière étape du processus de chargement de classe. Dans le processus de chargement de classe précédent, sauf que les applications utilisateur peuvent participer via un chargeur de classe personnalisé pendant la phase de chargement, les actions restantes sont entièrement dominées et contrôlées par le machine virtuelle. Dans la phase d'initialisation, le code Java défini dans la classe commencera effectivement à être exécuté.
Dans la phase de préparation, les variables ont reçu une fois la valeur initiale requise par le système, et dans la phase d'initialisation, les variables de classe et autres ressources sont initialisées en fonction de la zone de plan faite par le programmeur via le programme .
public class StaticTest { public static void main(String[] args) { staticFunction(); } static StaticTest st = new StaticTest(); static { System.out.println("1"); } { System.out.println("2"); } StaticTest() { System.out.println("3"); System.out.println("a="+a+",b="+b); } public static void staticFunction(){ System.out.println("4"); } int a=110; static int b =112; }
Quel est le résultat de l'exécution de ce code ?
La réponse est :
2 3 a=110,b=0 1 4
Pourquoi cela ? Vous souhaiterez peut-être réfléchir à ce qui suit :
Comprendre ce code ne consiste pas seulement à comprendre le mécanisme de chargement des classes de Java, mais aussi à comprendre la phase d'initialisation. L'initialisation des blocs de code statiques et des variables membres statiques est liée à l'ordre du code.
Le processus de chargement de classe est : chargement->connexion (vérification, préparation, analyse)->initialisation.
1. Dans la phase de préparation, la valeur par défaut sera définie pour la variable de classe, donc dans le cas un : st=null, b=0,
2. la classe sera exécutée en premier Constructeur,
En d'autres termes, il exécute simplement le bloc de code modifié statiquement et attribue des valeurs aux variables modifiées statiquement. L'ordre d'exécution des blocs de code modifiés statiques et des variables de classe est basé sur l'ordre dans lequel ils sont exécutés dans le fichier. Et static StaticTest st = new StaticTest() est classé en premier, donc new StaticTest() sera exécuté, ce qui consiste à initialiser l'objet
Pendant le processus d'initialisation de l'objet, les variables membres (bloc de code). ), puis exécutez la méthode constructeur. L'ordre d'exécution des variables membres est également celui qui la déclare en premier l'exécutera en premier, donc le premier bloc de code
2.2 Une fois la variable membre exécutée, la méthode constructeur est exécutée. À ce stade,a=110,b=0;
3. Le processus d'initialisation du code non statique déclenché par static StaticTest st = new StaticTest(); continue, donc la sortie 1.
4. Le chargement complet de la classe se termine ici, le code est exécuté et 4 est affiché.
Regardez le suivant
public class StaticTest { public static void main(String[] args) { staticFunction(); } static { System.out.println("1"); } { System.out.println("2"); } StaticTest() { System.out.println("3"); System.out.println("a="+a+",b="+b); } public static void staticFunction(){ System.out.println("4"); } int a=110; static int b =112; static StaticTest st = new StaticTest(); //将这条语句放到最下面 }
change juste une déclaration, et le résultat de ce code est
1 2 3 a=110,b=112 4
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!