Maison > Java > javaDidacticiel > Mécanisme de chargement de classe Java

Mécanisme de chargement de classe Java

(*-*)浩
Libérer: 2019-08-27 16:21:19
avant
2647 Les gens l'ont consulté

Pendant longtemps, j'ai été très réfractaire au mécanisme de chargement de classe de Java car je le trouvais trop difficile à comprendre. Mais pour devenir un bon ingénieur Java, j’ai décidé de mordre la balle et de l’étudier.

Mécanisme de chargement de classe Java

01. Bytecode

Avant de parler du mécanisme de chargement de classe Java, vous devez d'abord comprendre le bytecode Java, car c'est le cas. étroitement lié au mécanisme de chargement de classe.

Les ordinateurs ne comprennent que 0 et 1, donc les programmes écrits dans n'importe quel langage doivent être compilés en code machine pour être compris puis exécutés par l'ordinateur, et Java ne fait pas exception.

Java a crié un slogan très génial à sa naissance : "Write Once, Run Anywhere". Afin d'atteindre cet objectif, Sun a publié de nombreux logiciels pouvant fonctionner sur différentes plateformes (Windows, Linux) Le Java. Machine virtuelle (JVM) - responsable du chargement et de l'exécution du bytecode compilé Java.

Mécanisme de chargement de classe Java

À quoi ressemble le bytecode Java ? Jetons-y un coup d'œil à l'aide d'un code simple.

Le code source est le suivant :

package com.cmower.java_demo;

public class Test {

    public static void main(String[] args) {
        System.out.println("版权声明");
    }

}
Copier après la connexion

Une fois le code compilé, vérifiez le fichier de bytecode via la commande xxd Test.class.

xxd Test.class
00000000: cafe babe 0000 0034 0022 0700 0201 0019  .......4."......
00000010: 636f 6d2f 636d 6f77 6572 2f6a 6176 615f  com/cmower/java_
00000020: 6465 6d6f 2f54 6573 7407 0004 0100 106a  demo/Test......j
00000030: 6176 612f 6c61 6e67 2f4f 626a 6563 7401  ava/lang/Object.
00000040: 0006 3c69 6e69 743e 0100 0328 2956 0100  ..<init>...()V..
00000050: 0443 6f64 650a 0003 0009 0c00 0500 0601  .Code...........
00000060: 000f 4c69 6e65 4e75 6d62 6572 5461 626c  ..LineNumberTabl
Copier après la connexion

Vous vous sentez un peu confus, n'est-ce pas ?

C'est vrai.

Le café babe dans ce bytecode est appelé le "numéro magique", qui est un signe permettant à la JVM de reconnaître les fichiers .class. Le personnalisateur du format de fichier est libre de choisir le nombre magique (à condition qu'il ne soit pas utilisé auparavant), par exemple, le nombre magique pour les fichiers .png est 8950 4e47.

Quant aux autres contenus, vous pouvez choisir de les oublier.

02. Processus de chargement de classe

Après avoir compris le bytecode Java, parlons du processus de chargement de classe Java.

Le processus de chargement de classe Java peut être divisé en 5 étapes : chargement, vérification, préparation, analyse et initialisation. Ces 5 phases se déroulent généralement de manière séquentielle, mais dans le cas d'une liaison dynamique, la phase d'analyse intervient après la phase d'initialisation.

1) Chargement

L'objectif principal de la JVM à ce stade est de transférer le bytecode de différentes sources de données (peut-être des fichiers de classe, des packages jar ou même des réseaux) et de le convertir en un flux d'octets binaires et chargé en mémoire, et un objet java.lang.Class représentant la classe est généré.

2) Vérification

La JVM vérifiera le flux d'octets binaires à ce stade. Seuls ceux qui sont conformes aux spécifications du bytecode de la JVM peuvent être correctement exécutés par la JVM. Cette étape est une barrière importante pour assurer la sécurité de la JVM. Voici quelques contrôles majeurs.

Assurez-vous que le format du flux d'octets binaires est comme prévu (par exemple s'il commence par café bene).

Si toutes les méthodes sont conformes aux restrictions des mots clés de contrôle d'accès.

Si le nombre et le type de paramètres dans l'appel de méthode sont corrects.

Assurez-vous que les variables sont correctement initialisées avant utilisation.

Vérifiez si la variable reçoit une valeur du type approprié.

3) Préparation

La JVM allouera de la mémoire et initialisera les variables de classe (également appelées variables statiques, modifiées par le mot-clé static) à cette étape (correspondant à l'initialisation par défaut du type de données) valeur , comme 0, 0L, null, false, etc.).

En d'autres termes, s'il existe un tel morceau de code :

public String chenmo = "沉默";
public static String wanger = "王二";
public static final String cmower = "沉默王二";
Copier après la connexion

chenmo ne se verra pas allouer de mémoire, mais Wanger le sera, mais la valeur initiale de Wanger n'est pas "王二" mais nul.

Il est à noter que les variables modifiées par static final sont appelées constantes, qui sont différentes des variables de classe. Une fois qu'une constante reçoit une valeur, elle ne changera pas, donc la valeur de cpower dans la phase de préparation est "silent king two" au lieu de null.

4) Résolution

Cette étape convertit les références de symboles dans le pool constant en références directes.

quoi ? Référence symbolique, référence directe ?

La référence aux symboles utilise un ensemble de symboles (n'importe quelle forme de littéral, à condition qu'il puisse être localisé sans ambiguïté par rapport à la cible lorsqu'il est utilisé) pour décrire la cible référencée.

Au moment de la compilation, les classes Java ne connaissent pas l'adresse réelle de la classe référencée, elles ne peuvent donc utiliser que des références symboliques à la place. Par exemple, la classe com.Wanger fait référence à la classe com.Chenmo Lors de la compilation, la classe Wanger ne connaît pas l'adresse mémoire réelle de la classe Chenmo, elle ne peut donc utiliser que le symbole com.Chenmo.

La référence directe analyse la référence du symbole pour trouver l'adresse mémoire réelle de la référence.

5) Initialisation

Cette phase est la dernière étape du processus de chargement de la classe. Dans la phase de préparation, les variables de classe se sont vu attribuer des valeurs initiales par défaut, et dans la phase d'initialisation, les variables de classe se verront attribuer les valeurs attendues par le code. En d’autres termes, la phase d’initialisation est le processus d’exécution de la méthode constructeur de classe.

Oh, non, le paragraphe ci-dessus est très abstrait et difficile à comprendre, n'est-ce pas ?

String cmower = new String("Silent Wang Er");

Le code ci-dessus utilise le nouveau mot-clé pour instancier un objet chaîne, puis à ce moment, String sera appelé Le constructeur de la classe instancie le pouvoir.

03. Chargeur de classe

Après avoir parlé du processus de chargement de classe, nous devons parler du chargeur de classe.

一般来说,Java 程序员并不需要直接同类加载器进行交互。JVM 默认的行为就已经足够满足大多数情况的需求了。不过,如果遇到了需要和类加载器进行交互的情况,而对类加载器的机制又不是很了解的话,就不得不花大量的时间去调试

ClassNotFoundException 和 NoClassDefFoundError 等异常。

对于任意一个类,都需要由它的类加载器和这个类本身一同确定其在 JVM 中的唯一性。也就是说,如果两个类的加载器不同,即使两个类来源于同一个字节码文件,那这两个类就必定不相等(比如两个类的 Class 对象不 equals)。

站在程序员的角度来看,Java 类加载器可以分为三种。

1)启动类加载器(Bootstrap Class-Loader),加载 jre/lib 包下面的 jar 文件,比如说常见的 rt.jar。

2)扩展类加载器(Extension or Ext Class-Loader),加载 jre/lib/ext 包下面的 jar 文件。

3)应用类加载器(Application or App Clas-Loader),根据程序的类路径(classpath)来加载 Java 类。

来来来,通过一段简单的代码了解下。

public class Test {

	public static void main(String[] args) {
		ClassLoader loader = Test.class.getClassLoader();
		while (loader != null) {
			System.out.println(loader.toString());
			loader = loader.getParent();
		}
	}

}
Copier après la connexion

每个 Java 类都维护着一个指向定义它的类加载器的引用,通过 类名.class.getClassLoader() 可以获取到此引用;然后通过 loader.getParent() 可以获取类加载器的上层类加载器。

这段代码的输出结果如下:

sun.misc.Launcher$AppClassLoader@73d16e93
sun.misc.Launcher$ExtClassLoader@15db9742
Copier après la connexion

第一行输出为 Test 的类加载器,即应用类加载器,它是 sun.misc.Launcher$AppClassLoader 类的实例;第二行输出为扩展类加载器,是 sun.misc.Launcher$ExtClassLoader 类的实例。那启动类加载器呢?

按理说,扩展类加载器的上层类加载器是启动类加载器,但在我这个版本的 JDK 中, 扩展类加载器的 getParent() 返回 null。所以没有输出。

04、双亲委派模型

如果以上三种类加载器不能满足要求的话,程序员还可以自定义类加载器(继承 java.lang.ClassLoader 类),它们之间的层级关系如下图所示。

Mécanisme de chargement de classe Java

这种层次关系被称作为双亲委派模型:如果一个类加载器收到了加载类的请求,它会先把请求委托给上层加载器去完成,上层加载器又会委托上上层加载器,一直到最顶层的类加载器;如果上层加载器无法完成类的加载工作时,当前类加载器才会尝试自己去加载这个类。

PS:双亲委派模型突然让我联想到朱元璋同志,这个同志当上了皇帝之后连宰相都不要了,所有的事情都亲力亲为,只有自己没精力没时间做的事才交给大臣们去干。

使用双亲委派模型有一个很明显的好处,那就是 Java 类随着它的类加载器一起具备了一种带有优先级的层次关系,这对于保证 Java 程序的稳定运作很重要。

上文中曾提到,如果两个类的加载器不同,即使两个类来源于同一个字节码文件,那这两个类就必定不相等——双亲委派模型能够保证同一个类最终会被特定的类加载器加载。

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!

Étiquettes associées:
source:csdn.net
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal