Le style de vie des fichiers de classe Java

Bienvenue dans un autre épisode de "Under the Hood". Dans l'article du mois dernier, j'ai discuté de la machine virtuelle Java, ou JVM, l'ordinateur abstrait pour lequel tous les programmes Java sont compilés. Si vous n'êtes pas familier avec la JVM, vous voudrez peut-être lire l'article du mois dernier avant celui-ci. Dans cet article, je donne un aperçu de la structure de base et du mode de vie du fichier de classe Java.

Né pour voyager

Le fichier de classe Java est un format défini avec précision pour Java compilé. Le code source Java est compilé dans des fichiers de classe qui peuvent être chargés et exécutés par n'importe quelle machine virtuelle Java. Les fichiers de classe peuvent voyager sur un réseau avant d'être chargés par la JVM.

En fait, si vous lisez cet article via un navigateur compatible Java, les fichiers de classe de l'applet de simulation à la fin de l'article volent sur Internet vers votre ordinateur en ce moment. Si vous souhaitez les écouter (et que votre ordinateur dispose d'une capacité audio), appuyez sur le bouton suivant:

Vous avez besoin d'un navigateur compatible Java pour afficher cette applet

On dirait qu'ils s'amusent, hein? C'est dans leur nature. Les fichiers de classe Java ont été conçus pour bien voyager. Ils sont indépendants de la plateforme, ils seront donc les bienvenus dans plus d'endroits. Ils contiennent des bytecodes, le jeu d'instructions compact pour la JVM, afin qu'ils puissent voyager léger. Les fichiers de classe Java parcourent constamment les réseaux à une vitesse vertigineuse pour arriver aux JVM du monde entier.

Que contient un fichier de classe?

Le fichier de classe Java contient tout ce dont une JVM a besoin de savoir sur une classe ou une interface Java. Dans leur ordre d'apparition dans le fichier de classe, les composants principaux sont: magie, version, pool de constantes, indicateurs d'accès, cette classe, super classe, interfaces, champs, méthodes et attributs.

Les informations stockées dans le fichier de classe varient souvent en longueur, c'est-à-dire que la longueur réelle des informations ne peut pas être prédite avant de charger le fichier de classe. Par exemple, le nombre de méthodes répertoriées dans le composant de méthodes peut différer entre les fichiers de classe, car il dépend du nombre de méthodes définies dans le code source. Ces informations sont organisées dans le fichier de classe en faisant précéder les informations réelles de leur taille ou de leur longueur. De cette façon, lorsque la classe est chargée par la JVM, la taille des informations de longueur variable est lue en premier. Une fois que la machine virtuelle Java connaît la taille, elle peut lire correctement les informations réelles.

Les informations sont généralement écrites dans le fichier de classe sans espace ni remplissage entre les informations consécutives; tout est aligné sur les limites d'octets. Cela permet de garder les fichiers de classe petits afin qu'ils soient aérodynamiques lorsqu'ils volent à travers les réseaux.

L'ordre des composants du fichier de classe est strictement défini afin que les JVM puissent savoir à quoi s'attendre, et où l'attendre, lors du chargement d'un fichier de classe. Par exemple, chaque JVM sait que les huit premiers octets d'un fichier de classe contiennent les numéros de magie et de version, que le pool de constantes démarre sur le neuvième octet et que les indicateurs d'accès suivent le pool de constantes. Mais comme le pool de constantes est de longueur variable, il ne sait pas exactement où se trouvent les indicateurs d'accès tant qu'il n'a pas fini de lire dans le pool de constantes. Une fois qu'il a fini de lire dans le pool de constantes, il sait que les deux octets suivants seront les indicateurs d'accès.

Numéros de magie et de version

Les quatre premiers octets de chaque fichier de classe sont toujours 0xCAFEBABE. Ce nombre magique facilite l'identification des fichiers de classe Java, car les chances sont minces que les fichiers non-classe commencent par les mêmes quatre octets initiaux. Le nombre est appelé magique car il peut être retiré d'un chapeau par les concepteurs de format de fichier. La seule exigence est qu'il ne soit pas déjà utilisé par un autre format de fichier qui peut être rencontré dans le monde réel. Selon Patrick Naughton, membre clé de l'équipe Java d'origine, le nombre magique a été choisi "bien avant que le nom Java ne soit prononcé en référence à ce langage. Nous recherchions quelque chose d'amusant, d'unique et facile à retenir. C'est ce n'est qu'une coïncidence qu'OxCAFEBABE, une référence oblique aux mignons baristas de Peet's Coffee, préfigurait le nom de Java. "

Les quatre octets suivants du fichier de classe contiennent les numéros de version majeure et mineure. Ces numéros identifient la version du format de fichier de classe à laquelle un fichier de classe particulier adhère et permettent aux JVM de vérifier que le fichier de classe est chargeable. Chaque JVM a une version maximale qu'elle peut charger, et les JVM rejetteront les fichiers de classe avec les versions ultérieures.

Piscine constante

Le fichier de classe stocke les constantes associées à sa classe ou à son interface dans le pool de constantes. Certaines constantes qui peuvent être vues gambader dans le pool sont des chaînes littérales, des valeurs de variables finales, des noms de classe, des noms d'interface, des noms et types de variables, et des noms de méthodes et des signatures. Une signature de méthode est son type de retour et son ensemble de types d'arguments.

Le pool de constantes est organisé sous la forme d'un tableau d'éléments de longueur variable. Chaque constante occupe un élément du tableau. Dans tout le fichier de classe, les constantes sont référencées par l'index entier qui indique leur position dans le tableau. La constante initiale a un index de un, la seconde constante a un index de deux, etc. Le tableau de pool de constantes est précédé de sa taille de tableau, ainsi les JVM sauront combien de constantes attendre lors du chargement du fichier de classe.

Chaque élément du pool de constantes commence par une balise d'un octet spécifiant le type de constante à cette position dans le tableau. Une fois qu'une JVM saisit et interprète cette balise, elle sait ce qui suit la balise. Par exemple, si une balise indique que la constante est une chaîne, la machine virtuelle Java s'attend à ce que les deux octets suivants correspondent à la longueur de la chaîne. Après cette longueur de deux octets, la machine virtuelle Java s'attend à trouver la longueur nombre d'octets, qui constituent les caractères de la chaîne.

Dans le reste de l'article, je ferai parfois référence au nième élément du tableau de pool constant comme constant_pool [n]. Cela a du sens dans la mesure où le pool de constantes est organisé comme un tableau, mais gardez à l'esprit que ces éléments ont des tailles et des types différents et que le premier élément a un index de un.

Drapeaux d'accès

Les deux premiers octets après le pool de constantes, les indicateurs d'accès, indiquent si ce fichier définit ou non une classe ou une interface, si la classe ou l'interface est publique ou abstraite, et (si c'est une classe et non une interface) si la classe est définitive.

Cette classe

Les deux octets suivants, le composant this class , sont un index dans le tableau de pool constant. La constante référencée par cette classe , constant_pool [this_class], comporte deux parties, une balise à un octet et un index de nom à deux octets. La balise sera égale à CONSTANT_Class, une valeur qui indique que cet élément contient des informations sur une classe ou une interface. Constant_pool [nom_index] est une constante de chaîne contenant le nom de la classe ou de l'interface.

Le composant de cette classe donne un aperçu de la façon dont le pool de constantes est utilisé. Cette classe elle-même n'est qu'un index dans le pool de constantes. Lorsqu'une JVM recherche constant_pool [this_class], elle trouve un élément qui s'identifie en tant que CONSTANT_Class avec sa balise. La JVM sait que les éléments CONSTANT_Class ont toujours un index à deux octets dans le pool de constantes, appelé index de nom, après leur balise à un octet. Il recherche donc constant_pool [nom_index] pour obtenir la chaîne contenant le nom de la classe ou de l'interface.

Super classe

Après le composant de cette classe se trouve le composant de la super classe , un autre index de deux octets dans le pool de constantes. Constant_pool [super_class] est un élément CONSTANT_Class qui pointe vers le nom de la super classe d'où cette classe descend.

Interfaces

Le composant interfaces commence par un décompte de deux octets du nombre d'interfaces implémentées par la classe (ou interface) définie dans le fichier. Immédiatement après se trouve un tableau qui contient un index dans le pool de constantes pour chaque interface implémentée par la classe. Chaque interface est représentée par un élément CONSTANT_Class dans le pool de constantes qui pointe vers le nom de l'interface.

Des champs

Le composant de champs commence par un décompte de deux octets du nombre de champs de cette classe ou interface. Un champ est une instance ou une variable de classe de la classe ou de l'interface. Après le décompte se trouve un tableau de structures de longueur variable, une pour chaque champ. Chaque structure révèle des informations sur un champ, telles que le nom du champ, son type et, s'il s'agit d'une variable finale, sa valeur constante. Certaines informations sont contenues dans la structure elle-même et d'autres sont contenues dans des emplacements de pool constants pointés par la structure.

Les seuls champs qui apparaissent dans la liste sont ceux qui ont été déclarés par la classe ou l'interface définie dans le fichier; aucun champ hérité des super classes ou superinterfaces n'apparaît dans la liste.

Méthodes

Le composant de méthodes commence par un décompte de deux octets du nombre de méthodes dans la classe ou l'interface. Ce nombre inclut uniquement les méthodes qui sont explicitement définies par cette classe, et non les méthodes qui peuvent être héritées des superclasses. Après le décompte des méthodes se trouvent les méthodes elles-mêmes.

La structure de chaque méthode contient plusieurs informations sur la méthode, y compris le descripteur de méthode (son type de retour et sa liste d'arguments), le nombre de mots de pile requis pour les variables locales de la méthode, le nombre maximum de mots de pile requis pour l'opérande de la méthode stack, une table d'exceptions interceptées par la méthode, la séquence de bytecode et une table de numéros de ligne.

Les attributs

Les attributs, qui donnent des informations générales sur la classe ou l'interface particulière définie par le fichier, sont à l'arrière-plan. La section des attributs a un décompte sur deux octets du nombre d'attributs, suivi des attributs eux-mêmes. Par exemple, un attribut est l'attribut de code source; il révèle le nom du fichier source à partir duquel ce fichier de classe a été compilé. Les machines virtuelles Java ignoreront silencieusement les attributs qu'elles ne reconnaissent pas.

Chargement: une simulation d'un fichier de classe atteignant sa destination JVM

L'applet ci-dessous simule une JVM chargeant un fichier de classe. Le fichier de classe chargé dans la simulation a été généré par le compilateur javac avec le code source Java suivant:

class Act {public static void doMathForever () {int i = 0; tandis que (vrai) {i + = 1; i * = 2; }}}

L'extrait de code ci-dessus provient de l'article du mois dernier sur la JVM. C'est la même méthode doMathForever () exécutée par l'applet EternalMath de l'article du mois dernier. J'ai choisi ce code pour fournir un exemple réel qui n'était pas trop complexe. Bien que le code ne soit pas très utile dans le monde réel, il se compile dans un fichier de classe réel, qui est chargé par la simulation ci-dessous.

L'applet GettingLoaded vous permet de piloter la simulation de charge de classe une étape à la fois. Pour chaque étape du processus, vous pouvez en savoir plus sur le prochain bloc d'octets qui est sur le point d'être consommé et interprété par la JVM. Appuyez simplement sur le bouton "Step" pour que la JVM consomme le morceau suivant. Appuyer sur «Retour» annulera l'étape précédente, et appuyer sur «Réinitialiser» ramènera la simulation à son état d'origine, vous permettant de recommencer depuis le début.

La JVM est affichée en bas à gauche et consomme le flux d'octets qui compose le fichier de classe Act.class. Les octets sont affichés en flux hexadécimal depuis un serveur en bas à droite. Les octets se déplacent de droite à gauche, entre le serveur et la JVM, un morceau à la fois. Le bloc d'octets à consommer par la JVM lors de la prochaine pression sur le bouton «Step» est affiché en rouge. Ces octets en surbrillance sont décrits dans la grande zone de texte au-dessus de la JVM. Tous les octets restants au-delà du segment suivant sont affichés en noir.

J'ai essayé d'expliquer complètement chaque morceau d'octets dans la zone de texte. Il y a donc beaucoup de détails dans la zone de texte et vous souhaiterez peut-être parcourir toutes les étapes d'abord pour avoir une idée générale, puis revenir en arrière pour plus de détails.

Bon clic.

Vous avez besoin d'un navigateur compatible Java pour afficher cette applet.

Cliquez ici pour le code source de GettingLoaded. Pour exécuter cette applet par vous-même, vous aurez également besoin des deux fichiers que cette applet récupère du serveur, du fichier ASCII qui contient le texte de chaque étape et du fichier Act.class lui-même. Cliquez ici pour le code source de l'applet audio Flying Class Files.

FIN: Les petits caractères: "The Java Class File Lifestyle" Article Copyright (c) 1996 Bill Venners. Tous les droits sont réservés. Applet "GettingLoaded" Copyright (c) 1996 Artima Software Company. Tous les droits sont réservés.

: END_ENDREMARQUE

Bill Venners est président de Artima Software Company. Grâce à Artima, il fait du développement de logiciels personnalisés et du conseil.

En savoir plus sur ce sujet

  • La spécification Java Virtual Machine, le mot officiel de Sun.

    //java.sun.com/1.0alpha3/doc/vmspec/vmspec_1.html

  • À sa sortie, le livre The Java Virtual Machine Specification , //www.aw.com/cp/lindholm-yellin.html, de Tim Lindholm et Frank Yellin (ISBN 0-201-63452-X), qui fait partie de The Java Series, //www.aw.com/cp/javaseries.html), d'Addison-Wesley, sera probablement la meilleure ressource JVM.
  • Un brouillon du chapitre 4 de la spécification de la machine virtuelle Java , qui décrit le format de fichier de classe et le vérificateur de bytecode, peut être récupéré à partir de JavaSoft.

    //java.sun.com/java.sun.com/newdocs.html

Cette histoire, "Le style de vie des fichiers de classe Java" a été publiée à l'origine par JavaWorld.