Le format de fichier Class utilise une pseudo-structure similaire à la structure du langage C pour stocker les données. Il n'y a que deux types de données dans cette pseudo-structure : "nombre non signé" et "table".
·Les nombres non signés sont des types de données de base, avec u1, u2, u4 et u8 représentant respectivement 1 octet, 2 octets, 4 octets et 8 octets de nombres non signés. Les nombres signés et les nombres non signés peuvent être. utilisé pour décrire des nombres, des références d'index, des valeurs quantitatives ou des valeurs de chaîne codées en UTF-8.
·Une table est un type de données composite composé de plusieurs nombres non signés ou d'autres tables comme éléments de données Afin de faciliter la différenciation, les noms de toutes les tables se terminent généralement par "_info". Les tableaux sont utilisés pour décrire des données avec des structures composites hiérarchiques. L'ensemble du fichier de classe peut essentiellement être considéré comme un tableau. appelé le Nombre Magique, et sa seule fonction est de déterminer si ce fichier est un fichier de Classe pouvant être accepté par la machine virtuelle. Non seulement les fichiers de classe, mais de nombreuses normes de format de fichier ont l'habitude d'utiliser des nombres magiques pour l'identification. Par exemple, les formats d'image tels que GIF ou JPEG ont des nombres magiques dans l'en-tête du fichier.
Le nombre magique du fichier Class est très "romantique", la valeur est 0xCAFEBABE (café bébé ?)
Les 4 octets du nombre magique sont suivis de Class. Numéro de version du fichier : les 5e et 6e octets sont le numéro de version mineure (MinorVersion), et les 7e et 8e octets sont le numéro de version majeure (Major Version). Le numéro de version de Java commence à 45. Le numéro de version principal de chaque version majeure du JDK publiée après le JDK 1.1 est augmenté de 1 (JDK 1.0~1.1 utilise les numéros de version 45.0~45.3). Les versions supérieures du JDK peuvent être rétrocompatibles avec les versions précédentes). . version du fichier Class, mais vous ne pouvez pas exécuter des versions ultérieures du fichier Class,Constant Pool
Immédiatement après le majeur et le mineur. les numéros de version sont l'entrée du pool constant. Le pool constant peut être comparé à l'entrepôt de ressources dans le fichier de classe. Il s'agit des données les plus associées aux autres projets dans la structure du fichier de classe. espace dans le fichier de classe. De plus, il se trouve également dans le fichier de classe. Le premier élément de données de type tableau qui apparaît dans .Le pool de constantes stocke principalement deux types de constantes : les littérales (Literal) et les références symboliques (Symbolic References). Les littéraux sont plus proches de la notion de constantes au niveau du langage Java, comme les chaînes de texte, les valeurs constantes déclarées comme finales, etc. Les références de symboles appartiennent au concept de principes de compilation, et incluent principalement les types de constantes suivants :
·Package exporté ou ouvert par le module ·Classes et interfaces Nom complet (Nom complet) ·Nom et descripteur du champ (Descripteur)·Nom et descripteur de la méthode· Descripteur de méthode et type de méthode (Méthode Handle, Type de méthode, Invocation dynamique)·Site d'appel calculé dynamiquement, Constante calculée dynamiquement) Lorsque le code Java est compilé avec Javac, il n'y a pas de "connexion" comme C et C++. Au lieu de cela, il est connecté dynamiquement lorsque la machine virtuelle charge le fichier Class (voir le chapitre 7 pour plus de détails). En d'autres termes, les informations de disposition finale de chaque méthode et champ en mémoire ne seront pas enregistrées dans le fichier Classe. Si les références symboliques de ces champs et méthodes ne sont pas converties par la machine virtuelle au moment de l'exécution, l'adresse réelle d'entrée en mémoire ne peut pas être. obtenu, c'est-à-dire qu'il ne peut pas être utilisé directement par la machine virtuelle. Lorsque la machine virtuelle charge une classe, elle obtiendra la référence de symbole correspondante à partir du pool de constantes, puis l'analysera et la traduira vers l'adresse mémoire spécifique lors de la création ou de l'exécution de la classe. Chaque constante du pool de constantes est une table. Initialement, il y avait 11 données de structure de table avec des structures différentes dans la table constante. Plus tard, afin de mieux prendre en charge les appels de langage dynamiques, 4 supplémentaires ont été ajoutées. Il existe deux constantes liées aux langages dynamiques [1]. Afin de prendre en charge le système modulaire Java (Jigsaw), deux constantes, CONSTANT_Module_info et CONSTANT_Package_info, ont été ajoutées. Par conséquent, à partir du JDK13, il existe 17 types de constantes différents. dans le tableau des constantes.
D'ailleurs, puisque les méthodes et les champs du fichier Class doivent faire référence à la constante de type CONSTANT_Utf8_info pour décrire le nom, la longueur maximale de la constante de type CONSTANT_Utf8_info est également la longueur maximale des noms de méthodes et de champs en Java. La longueur maximale ici est la valeur maximale de length, qui est la valeur maximale 65535 que le type u2 peut exprimer. Par conséquent, si un programme Java définit un nom de variable ou de méthode dépassant 64 Ko de caractères anglais, il ne sera pas compilé même si les règles et tous les caractères sont légaux.
Classfile /D:/BaiduYunDownload/geekbang-lessons/thinking-in-spring/validation/target/classes/org/geekbang/thinking/in/spring/validation/TestClass.class
Dernière modification le 25/06/2020 ; taille 439 octets Somme de contrôle MD5 18760ee8065f9fb68d4dab7bd7450c4c Compilé à partir de "TestClass.java" #🎜 🎜#public classe org.geekbang.thinking.in.spring.validation.TestClass
version mineure : 0
version majeure : 52
drapeaux : ACC_PUBLIC, ACC_SUPER
Piscine constante :
#1 = Methodref #4.#18 // java/lang/Object."
#2 = Fieldref #3.#19 // org/geekbang/thinking/in/spring // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8
#8 = ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lorg/geekbang/thinking/in/spring/validation/TestClass;
#14 = Utf8 inc
#15 = Utf8 ()I
#16 = Utf8 Fichier Source
#17 = Utf8 TestClass.java
#18 = NameAndType #8 // "
#19 = NameAndType #5 : #6 // m:I
# 20 = UTF8 ORG / GeekBang / Thinking / in / Spring / Validation / TestClass
# 21 = UTF8 Java / Lang / Object
{
public org.geekbang.thinking.in.spring.validation.testClass ();
descripteur : ()V
flags : ACC_PUBLIC
Code :
stack=1, locaux=1, args_size=1
0 : 1 : Invocationspecial #1 // Méthode java/lang/Object. "
4 : retour
LineNumberTable:
ligne 3 : 0
LocalVariableTable :
Début Longueur Slot Nom Signature
0 5 0 ce Lorg/geekbang/thinking/in/ spring/validation/TestClass;
public int inc();
descripteur : ()I
flags : ACC_PUBLIC
Code :
stack=2, locals=1, args_size=1
0 : aload_0
1: Getfield # 2 // Field m: i
4: iconst_1
5: iadd
6: ireturn
lineNumberTable:
Ligne 7: 0
localvariabbletable:
start Longueur Nom de l'emplacement Signature
0 7 0 this Lorg/geekbang/thinking/in/spring/validation/TestClass;
}
SourceFile : "TestClass.java"
Après la fin du pool de constantes, les 2 octets suivants représentent les indicateurs d'accès (access_flags ), cet indicateur est utilisé pour identifier certaines informations d'accès au niveau de la classe ou de l'interface, notamment : si cette classe est une classe ou une interface, si elle est définie comme un type public ; si elle est définie comme un type abstrait ; class, s'il est déclaré comme final ;
L'index de classe, l'index de classe parent et l'ensemble d'index d'interface sont tous classés dans l'ordre après l'indicateur d'accès. L'index de classe et l'index de classe parent sont chacun représentés par deux valeurs d'index de type u2. pointez vers une constante de descripteur de classe de type CONSTANT_Class_info, via CONSTANT_Class_info. La valeur d'index dans la constante de type peut être trouvée dans la chaîne de nom complet définie dans la constante de type CONSTANT_Utf8_info.
Il a fallu attendre l'émergence des expressions Lambda et des méthodes d'interface par défaut dans le JDK 8 pour que l'instruction InvokeDynamic entre en jeu dans les fichiers Class générés par le langage Java
Cet attribut ajouté dans le JDK 8 permet donc au compilateur de
.(Ajoutez le paramètre -parameters lors de la compilation) Le nom de la méthode est également écrit dans le fichier Class, et MethodParameters est un attribut de la table des méthodes. Il est au même niveau que l'attribut Code et peut être obtenu via l'API de réflexion sur. exécution.
·Charger une variable locale dans la pile d'opérandes : iload
·Stocker une valeur de la pile d'opérandes dans la table de variables locales : istore
·Charger une constante dans la pile d'opérandes : bipush
iload_
·Instructions d'addition : iadd, ladd, fadd, dadd
·Instructions de soustraction : isub, lsub, fsub, dsub
·Instructions de multiplication : imul, lmul, fmul, dmul
·Instructions de division : idiv, ldiv, fdiv, ddiv
·Instructions restantes : irem, lrem, frem, drem
·Instructions de négation : ineg, lneg, fneg, dneg
·Instructions de déplacement : ishl, ishr, iushr, lshl, lshr, lushr
·Instructions OR au niveau du bit : ior, lor
·Instructions AND au niveau du bit : iand, land
·Instructions XOR au niveau du bit : ixor, lxor
·Variables locales Instructions d'auto-incrémentation : iinc
·Comparaison instructions : dcmpg, dcmpl, fcmpg, fcmpl, lcmp
La sémantique de l'instruction InvokeSpecial a été modifiée dans le JDK 1.0.2. JDK 7 a ajouté l'instruction Invokedynamic et a interdit les instructions ret et jsr.
Cycle de vie de la classeChargement-> Connexion (vérification, préparation, analyse)->Initialisation->Utilisation->Désinstallation.
L'ordre des cinq étapes de chargement, vérification, préparation, initialisation et déchargement est déterminé. Le processus de chargement du type doit démarrer étape par étape dans cet ordre, tandis que l'étape d'analyse ne l'est pas nécessairement : elle peut être initialisée dans certains cas. phase de cas avant le démarrage. Il s'agit de prendre en charge la fonctionnalité de liaison d'exécution du langage Java (également appelée liaison dynamique ou liaison tardive).
valeur int finale statique publique = 123 ;
Lors de la compilation, Javac générera l'attribut ConstantValue pour la valeur. Pendant la phase de préparation, la machine virtuelle attribuera la valeur à 123 en fonction du paramètre Con-stantValue.
Le processus de fonctionnement du modèle de délégation parent est le suivant : si un chargeur de classe reçoit une demande de chargement de classe, il n'essaiera pas de charger la classe lui-même en premier, mais déléguera la demande au chargeur de classe parent pour qu'il la termine, à chaque niveau. vrai pour tous les chargeurs de classe, donc toutes les demandes de chargement doivent éventuellement être envoyées au chargeur de classe de démarrage de niveau supérieur uniquement lorsque le chargeur parent indique qu'il ne peut pas terminer la demande de chargement (le fichier requis n'est pas trouvé dans sa classe de recherche). , le sous-chargeur tentera de terminer le chargement par lui-même
Tout d'abord, le Extension Class Loader est remplacé par le Platform Class Loader. Il s'agit en fait d'un changement très logique. Puisque l'ensemble du JDK est construit sur la base de la modularité (les fichiers rt.jar et tools.jar d'origine ont été divisés en dizaines de fichiers JMOD), la bibliothèque de classes Java est naturellement suffisante pour les exigences évolutives. il n'est pas nécessaire de conserver le répertoire
Toutes les actions de répartition qui s'appuient sur des types statiques pour déterminer la version d'exécution d'une méthode sont appelées répartition statique. L’application la plus typique de la répartition statique est la surcharge de méthodes. La répartition statique se produit pendant la phase de compilation, de sorte que l'action permettant de déterminer la répartition statique n'est pas réellement effectuée par la machine virtuelle. C'est pourquoi certains documents choisissent de la classer comme « analyse » plutôt que « répartition ».
La machine virtuelle Java prend en charge les 5 méthodes suivantes pour appeler des instructions de bytecode, qui sont :
·invokestatic. Utilisé pour appeler des méthodes statiques.
·invokespecial. Utilisé pour appeler la méthode
·invoquer le virtuel. Utilisé pour appeler toutes les méthodes virtuelles.
·invokeinterface. Utilisé pour appeler des méthodes d'interface, un objet qui implémente l'interface sera déterminé au moment de l'exécution.
·invoquerdynamique. La méthode référencée par le qualificateur de site d'appel est d'abord résolue dynamiquement au moment de l'exécution, puis la méthode est exécutée. La logique de répartition des quatre premières instructions d'appel est solidifiée à l'intérieur de la machine virtuelle Java, tandis que la logique de répartition de l'instruction d'invocation dynamique est déterminée par la méthode de démarrage définie par l'utilisateur.
Tant que la méthode peut être appelée par les instructions Invokestatic et InvocationSpecial, la version appelante unique peut être déterminée lors de la phase d'analyse. Il existe quatre méthodes dans le langage Java qui remplissent cette condition : les méthodes statiques, les méthodes privées, les constructeurs d'instances. , et les méthodes de la classe parent, couplées à la méthode modifiée par final (bien qu'elle soit appelée à l'aide de l'instruction Invokevirtual), ces cinq appels de méthode résoudront la référence du symbole en une référence directe à la méthode lorsque la classe est chargée. Ces méthodes sont collectivement appelées « méthode non virtuelle » (méthode non virtuelle), au contraire, d'autres méthodes sont appelées « méthode virtuelle » (méthode virtuelle).
L'appel d'analyse doit être un processus statique, qui est entièrement déterminé lors de la compilation. Pendant la phase d'analyse du chargement de la classe, toutes les références de symboles impliquées seront converties en références directes claires, et il n'est pas nécessaire de le retarder jusqu'à l'exécution. L'autre forme principale d'appel de méthode : l'appel de répartition (Dispatch) est beaucoup plus compliqué. Il peut être statique ou dynamique. Selon le nombre de cas basés sur la répartition, il peut être divisé en répartition unique et multi-dispatch [1]. La combinaison de ces deux types de méthodes de répartition constitue quatre combinaisons de répartition : répartition unique statique, répartition multiple statique, répartition unique dynamique et répartition multiple dynamique. Examinons comment la répartition des méthodes est effectuée dans la machine virtuelle.
Le code définit délibérément deux variables avec le même type statique mais des types réels différents, mais la machine virtuelle (ou le compilateur pour être précis) transmet le paramètre
type statiqueau lieu du type réel comme lors d'une surcharge Base de jugement. Étant donné que le type statique est connu au moment de la compilation, pendant la phase de compilation, le compilateur Javac détermine quelle version surchargée sera utilisée en fonction du type statique du paramètre. Il sélectionne donc sayHello (Human) comme cible appelante et écrit le type symbolique. référence de cette méthode dans les paramètres des deux instructions invokevirtual de la méthode main().
Toutes les actions de répartition qui s'appuient sur des types statiques pour déterminer la version d'exécution d'une méthode sont appelées répartition statique.L'application la plus typique de la répartition statique est la surcharge de méthodes. La répartition statique se produit pendant la phase de compilation, de sorte que l'action permettant de déterminer la répartition statique n'est pas réellement effectuée par la machine virtuelle. C'est pourquoi certains documents choisissent de la classer comme « analyse » plutôt que « répartition ». On peut voir que la priorité de surcharge des paramètres de longueur variable est la plus basse. Les champs ne participent jamais au polymorphisme Lorsqu'une méthode d'une classe accède à un champ portant un certain nom, le nom fait référence au champ que la classe peut voir.
Point cléC'est précisément parce que la première étape de l'exécution de l'instruction invoquéevirtual est de déterminer le type réel du récepteur au moment de l'exécution, donc l'instruction invoquéevirtual dans les deux appels ne résout pas la référence symbolique du récepteur méthode dans le pool constant dans une référence directe C'est tout. La version de la méthode sera sélectionnée en fonction du type réel du récepteur de méthode. Ce processus est l'essence même de la réécriture de méthode dans le langage Java. Nous appelons ce processus de répartition qui détermine la version d'exécution de la méthode en fonction du type réel au moment de l'exécution, appelé répartition dynamique. La racine du polymorphisme réside dans la logique d'exécution de l'instruction d'appel de méthode virtuelle Invokevirtual. Naturellement, la conclusion que nous en tirerons ne sera valable que pour les méthodes et non pour les champs, car les champs n'utilisent pas cette instruction.
Le langage Java est un langage statique à répartition multiple et dynamique à répartition unique.
Pour faciliter la mise en œuvre du programme, les méthodes avec la même signature doivent avoir le même numéro d'index dans la table des méthodes virtuelles de la classe parent et de la sous-classe. De cette façon, lorsque le type est modifié, seule la table des méthodes virtuelles est consultée. up doit être modifié, puis les adresses d'entrée requises sont converties en fonction des index dans différentes tables de méthodes virtuelles. La table des méthodes virtuelles est généralement initialisée lors de la phase de connexion du chargement de la classe. Après avoir préparé les valeurs initiales des variables de la classe, la machine virtuelle initialise également la table des méthodes virtuelles de la classe.
Prise en charge des langages de type dynamique Le nombre de jeux d'instructions de bytecode de la machine virtuelle Java. Depuis l'avènement de la première machine virtuelle Java de Sun, il n'y a eu qu'une seule nouvelle instruction en plus de 20 ans. Il s'agit du premier jeu d'instructions de bytecode avec la sortie du JDK 7. Nouveau membre - invoque une instruction dynamique. Cette instruction nouvellement ajoutée est l'une des améliorations apportées pour atteindre l'objectif du projet du JDK 7 : implémenter la prise en charge du langage typé dynamiquement (Dynamically Typed Language). C'est également une réserve technique pour la mise en œuvre fluide des expressions Lambda dans le JDK 8. Qu'est-ce qu'un langage typé dynamiquement [1] ? La principale caractéristique d'un langage typé dynamiquement est que le processus principal de vérification de type est effectué pendant l'exécution plutôt que pendant la compilation. Il existe de nombreux langages qui répondent à cette fonctionnalité, notamment : APL, Clojure, Erlang, Groovy. , javaScript, Lisp, Lua, PHP, Prolog, Python, Ruby, Smalltalk, Tcl, et plus encore. En revanche, les langages qui effectuent une vérification de type lors de la compilation, tels que C++ et Java, sont les langages typés statiquement les plus couramment utilisés. Les variables n'ont pas de type, mais seules les valeurs des variables ont des types Fournir une prise en charge directe des types dynamiques au niveau de la machine virtuelle Java est devenu un problème qui doit être résolu pour le développement de la plateforme Java. Il s'agit de l'instruction d'invocation dynamique et de Java. .lang dans la proposition JSR-292 dans JDK 7 Le contexte technique de l'émergence du package .invoke. Le java.lang.invoke package [1] nouvellement ajouté dans JDK 7 est une partie importante de JSR 292. L'objectif principal de ce package est de remplacer le chemin précédent consistant simplement à s'appuyer sur des références de symboles pour déterminer la méthode cible. de l'appel. De plus, un nouveau mécanisme de détermination dynamique de la méthode cible est fourni, appelé "Method Handle". ·Les mécanismes Reflection et MethodHandle simulent essentiellement les appels de méthode, mais Reflection simule les appels de méthode au niveau du code Java, tandis que MethodHandle simule les appels de méthode au niveau du bytecode. Dans la structure de répertoires Tomcat, vous pouvez configurer 3 groupes de répertoires (/common/*, /server/* et /shared/*, mais ils ne sont pas forcément ouverts par défaut, et seul le répertoire /lib/* peut existent) pour stocker la bibliothèque de classes Java, en plus du répertoire "/WEB-INF/*" de l'application Web elle-même, soit un total de 4 groupes. Placez la bibliothèque de classes Java dans ces quatre groupes de répertoires. Chaque groupe a une signification indépendante, qui sont : ·Placez-la dans le répertoire /common. La bibliothèque de classes peut être utilisée par Tomcat et toutes les applications Web. ·Placez-le dans le répertoire /server. La bibliothèque de classes peut être utilisée par Tomcat et est invisible pour toutes les applications Web. ·Placez-le dans le répertoire /shared. La bibliothèque de classes peut être utilisée par toutes les applications Web, mais n'est pas visible par Tomcat lui-même. ·Placez-le dans le répertoire /WebApp/WEB-INF. La bibliothèque de classes ne peut être utilisée que par l'application Web et n'est pas visible par Tomcat ou d'autres applications Web. Afin de prendre en charge cette structure de répertoires et de charger et isoler les bibliothèques de classes dans le répertoire, Tomcat a personnalisé plusieurs chargeurs de classes, qui sont implémentés selon le modèle classique de délégation parent Chargeur de classe commun, chargeur de classe Catalina ( également appelé chargeur de classe serveur), le chargeur de classe partagé et le chargeur de classe Webapp sont les propres chargeurs de classe de Tomcat, qui chargent respectivement les bibliothèques de classes /common/*, /server/*, / dans shared/* et /WebApp/WEB-INF/. *. Il existe généralement plusieurs instances de chargeurs de classe WebApp et de chargeurs de classe JSP. Chaque application Web correspond à un chargeur de classe WebApp et chaque fichier JSP correspond à un chargeur de classe JasperLoader. La portée de chargement de JasperLoader est uniquement le fichier Class compilé par ce fichier JSP. Le but de son existence est d'être ignoré : lorsque le serveur détecte que le fichier JSP a été modifié, il remplacera l'instance JasperLoader actuelle et implémentera le. Fonction HotSwap des fichiers JSP en créant un nouveau chargeur de classe JSP.
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!