Architecture de la Machine Virtuelle Java
Architecture de la Machine Virtuelle Java
: H1588 V2
Mots-clés Résumé Le succès de Java l'a promu langage de programmation sur internet. Cet article
architecture présente une architecture synthétique et idéale de la machine virtuelle, ainsi que le
format du fichier compilé et un sous-ensemble du jeu d'instructions. Les instructions de la
machine sont regroupées par famille et leur description est associée aux codes sources
Java les ayant engendrées. Les avantages de cette solution sont connus et multiples,
l’inconvénient majeur restant le surcoût en temps d’exécution.
Keywords Abstract Due to its success Java has become the programming language of the Internet.
architecture This article presents a synthetic and ideal architecture of the virtual machine, together
with the format of the compiled file and a subset of the instruction set. The machine
instructions are grouped by family and their description is associated with the Java source
codes that have generated them. Although the advantages of this solution are known and
numerous, the major drawback remains the additional cost in execution time.
Par mail :
[Link]@[Link]
Par téléphone :
00 33 (0)1 53 35 20 20 © Techniques de l'Ingénieur | Tous droits réservés
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
3.3 Décompilateur............................................................................................. — 8
3.4 Chargement et édition des liens................................................................ — 9
3.5 Vérification du fichier compilé................................................................... — 9
4. Jeu d’instructions/source Java ........................................................... — 9
4.1 Exemples ..................................................................................................... — 9
4.2 Notation et format des instructions .......................................................... — 10
4.3 Évaluation d’expressions arithmétiques................................................... — 12
4.4 Opérations de manipulation de la pile...................................................... — 12
4.5 Variables locales ......................................................................................... — 12
4.6 Conversions implicites et explicites .......................................................... — 13
4.7 Expressions booléennes ............................................................................ — 13
4.8 Instructions de contrôles............................................................................ — 14
4.9 Instructions pour la sélection (switch/case) — 14
4.10 Variables de classes.................................................................................... — 14
4.11 Appel de méthodes de classes .................................................................. — 15
4.12 Allocation des objets, variables d’instance, appel du constructeur ....... — 16
4.13 Tableaux ...................................................................................................... — 17
4.14 Héritage, appel de méthode d’instance et liaison dynamique................ — 17
4.15 Interface ....................................................................................................... — 17
4.16 Test d’appartenance et conversion de type.............................................. — 17
4.17 Exceptions ................................................................................................... — 19
4.18 Threads ........................................................................................................ — 19
4.19 Optimisations .............................................................................................. — 19
5. Conclusion ................................................................................................ — 22
Pour en savoir plus ........................................................................................... Doc. H 1 588v2
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Java est simple. Sa syntaxe est basée sur les langages C et C++.
La machine Java est une machine langage : les choix L’absence de pointeurs améliore nettement la lisibilité des codes
d’architecture, le jeu d’instructions reflètent les concepts du sources. Un ramasse-miettes dispense le programmeur de la
langage Java. Certaines caractéristiques du langage doivent gestion de la mémoire.
être rappelées afin de mieux appréhender la machine virtuelle.
Java est fortement typé. L’affectation entre objets doit être
Nota : cette présentation simplifiée des concepts est extraite de [1] et [H 3 088]. effectuée entre deux objets liés entre eux par la relation d’héritage.
Les changements de type explicites sont vérifiés à l’exécution. Une
Java est un langage de programmation orienté objet. Cette exception est levée en cas d’erreur.
technique de programmation oriente le développement d’une
application par les types de données utilisés. Les noms des types Java permet la programmation distribuée. L’accès aux objets
sont en général extraits du contexte du projet. Une classification distants ainsi que la migration de code à travers Internet sont
de ces types de données analogue à la langue naturelle par la intégrés. L’introspection d’un objet fait partie du langage et permet
relation est-un est effectuée. L’héritage en programmation orientée entre autres de déclencher des méthodes d’un objet ayant migré
objet propose une implémentation de cette relation est-un. Un type d’un poste à l’autre. La génération dynamique de mandataire utili-
de données peut hériter d’un autre type, il possède alors en son sant l’introspection permet de simplifier les accès distants pour le
nom les attributs et les propriétés du type hérité. Si on appelle le développeur.
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
La sécurité est essentielle dans un environnement distribué. La La classe constatée appartient à l’arbre d’héritage établi à la
machine Java dispose d’un outil de vérification des données et du compilation. En Java, la racine de toute classe est la classe
code transmis par le réseau. Les navigateurs possèdent des [Link].
contraintes de sécurité supplémentaires. Des vérifications sont
également faites à l’exécution. ■ Exceptions et clause finally
Un mécanisme d’exceptions est présent en Java. Il est utilisé
Java propose la notion de processus léger, ou thread, et un
afin de gérer un traitement anormal en cours d’exécution. Le bloc
mécanisme de protection d’accès aux ressources dans un environ-
try/catch permet de filtrer l’exception et éventuellement de la
nement concurrent, ainsi que la notion d’événements liée à l’état
propager vers l’appelant. La clause finally regroupe un bloc
d’une ressource.
d’instructions qui est toujours exécuté après à la sortie du bloc
try/catch auquel cette clause est rattachée.
défaut. Le constructeur de la classe correspondante ainsi que tous bloqués. Un mécanisme d’attente et de notification, d’un
les constructeurs des classes héritées sont déclenchés à chaque événement lié à la valeur d’une ressource partagée, est hérité de la
allocation d’un objet. classe Object, mécanisme appelé variable condition.
■ Méthodes, héritage
Les méthodes de la classe fournissent le comportement des 1.3 Modèle de compilation,
instances de cette classe. La classe effective d’une instance ne indépendance de la plate-forme
peut être déterminée qu’à l’exécution ; cette classe, que l’on
nomme classe constatée afin de la distinguer de la classe déclarée Avant d’exécuter une application Java, le code source Java doit
dans la source de l’instance, reflète le comportement attendu par être compilé afin de produire un fichier objet reconnu par la
le programmeur. En Java, la sélection du comportement d’une machine virtuelle Java. Ce fichier est ensuite chargé depuis le
instance en fonction de la classe constatée est mise en œuvre par réseau ou localement. Toute plate-forme possédant une machine
défaut. Ce mécanisme de sélection est appelé liaison dynamique. virtuelle Java peut exécuter ce code (figure 1).
Fichier compilé
[Link] 1100 1010 1111 1110 1011 1010 1011 1110 0000 0011 0001 1101...
Lecture du fichier
locale ou distante
PC/ Mac PC 2
Windows MacOS Linux Interprétation
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
zone des environnements. L’implémentation de ces double. Le type Java boolean est représenté dans la machine par
composantes peut être réalisée par du matériel ou du logiciel. un entier de type int ou byte, l’entier 0 est associé à la valeur false,
L’architecture développée ici complète l’architecture de la une valeur différente de 0 correspond à la valeur true.
machine virtuelle proposée par Oracle [2]. Les valeurs de type reference sont inaccessibles au program-
meur, elles représentent les adresses en mémoire des objets. En
cours d’exécution, ces adresses sont obtenues à l’allocation des
objets. L’objet alloué est soit une instance de classe, soit un tableau.
Également invisible au programmeur, le type address
Réseau, Internet implémente une adresse dans le code de la méthode en cours. Les
Machine hôte,
valeurs de ce type sont utilisées lors de l’exécution du bloc de
Machine locale, système gestion des exceptions avec la clause finally.
disque, PROM... d’exploitation L’unité de base de la mémoire de la machine virtuelle est le mot.
Un mot doit être choisi afin qu’il puisse contenir une variable de
type Java byte, short, int, long ou char. Une implémentation d’un
mot en 32 bits nécessite deux mots pour les types long et double.
L’implémentation du mot correspond en général à la taille d’une
adresse de la machine hôte.
Chargeur Mémoire Librairies natives
dynamique d’objet et – fichiers
de classe ramasse-miettes – thread 2.2 Exécutif ou processeur virtuel
L’unité d’exécution est constituée d’une table contenant les
classes après chargement, de l’interpréteur des instructions et de
la zone des environnements.
Chaque classe contient le code des méthodes et leurs données,
ainsi que les variables statiques de la classe.
L’interpréteur d’instructions exécute les instructions de la
méthode en cours, avec l’environnement du processus léger ou
thread actif.
Exécutif
Chaque environnement contient :
Code et Interpréteur Environnements – une pile d’exécution qui reflète l’enchaînement des appels des
données des et piles méthodes ;
des classes instructions d’exécution – une pile d’évaluation qui est utilisée par l’interpréteur
d’instructions à la suite d’opérations arithmétiques ou logiques et
également lors du passage des paramètres ;
– une zone de contexte qui mémorise l’état de la machine et qui
contient en général les registres et toutes les informations liées au
Figure 2 – Système Java minimal et idéal thread actif.
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Tant que la fin du programme n’est pas rencontrée faire Environnement Theard 2 Theard 3
du theard 1 (actif)
Lecture d’une instruction, instruction = code [pc]
Environement par theard
En fonction du code opération de l’instruction
Lire éventuellement les opérandes depuis le code ou la pile
Figure 4 – Environnements et piles d’exécution
Effectuer les actions correspondantes,
Affecter le compteur ordinal en conséquence, pc = pc + n
Fin tant que
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
optop
optop optop
y
optop
x x x+y
empiler (x) empiler (y) additionner
pc Mémoire optop
me de code Interpréteur Environnements
frame
cl et données des et piles
vars
gvars des classes instructions d’exécution
pe
■ Pile d’évaluation depuis le réseau, charge les classes héritées, réalise l’édition des
Cette pile est utilisée lors de l’évaluation des expressions liens et vérifie le contenu sémantique et syntaxique des fichiers
arithmétiques ou logiques. Les valeurs retournées par les chargés. L’exécution des blocs statiques des classes chargées est
différentes instructions sont placées sur la pile. Les paramètres effectuée et enfin l’exécution des constructeurs.
transmis aux méthodes utilisent également la pile d’évaluation. Le Nota : le format du fichier et le chargeur de classe sont détaillés au paragraphe 3.
sommet de pile est référencé par le registre optop.
Parution : août 2013 - Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Exemple : la figure 6 décrit le calcul de l’expression x + y, soit la 2.4 Mémoire d’objets et ramasse-miettes
séquence des pseudo-instructions {empiler (x) ; empiler (y) ;
additionner}. Un objet est créé en Java par l’opérateur new. Une zone de la
taille nécessaire est réservée en mémoire des objets ; une
2.2.4 Registres, état de la machine référence sur cette zone est retournée, cette référence étant
affectée à la variable du programme.
Analogues aux registres d’un microprocesseur, les registres de La taille de l’objet est le cumul des tailles des données
la machine Java représentent l’état de la machine en cours d’instance de cette classe et des classes héritées ; d’autres
d’exécution (figure 7). Ils sont lus et modifiés par l’interpréteur informations sont ajoutées, comme la référence sur la classe
d’instructions. Les registres suivants sont issus de la documenta- d’appartenance de l’objet.
tion d’Oracle :
La structure interne d’une classe doit être compatible avec la
– pc : le compteur ordinal, adresse de l’instruction en cours ;
définition en Java de la classe [Link]. Le programmeur
– optop : un pointeur sur le sommet de la pile d’évaluation ; Java peut demander la classe constatée d’une instance. La
– frame : un pointeur sur le segment d’activation de la méthode méthode getClass( ) de la classe [Link] dont la signature
en cours ; est public final Class getClass( ) ; est exécutée, la référence retour-
– vars : un pointeur sur la première variable locale de la méthode
née étant de type Class. Toutes les informations de cette classe
en cours.
deviennent accessibles au programmeur Java via cette référence.
D’autres registres caractérisent l’état de la machine :
– un registre dénotant l’environnement courant associé au Exemple : la figure 8 illustre deux créations d’instance (obj et p) et
processus (pe) ; l’obtention de la classe constatée (p) ; obj reçoit p par affectation ; le
– un pointeur sur la classe courante (cl) ; type constaté de obj devient Point ; la structure précédemment
– la référence de la méthode courante (me) ; référencée devient récupérable par le ramasse-miettes (figure 8b ).
– un pointeur sur la zone de variables de la classe (gvar).
Ces registres, présents dans certaines implémentations, sont liés Le ramasse-miettes libère les zones qui ne sont plus référencées
aux choix d’implémentations de la machine virtuelle. par les variables du programme. Ces zones compactées deviennent
Les structures internes présentées sont celles d’une JVM idéale, libres pour une nouvelle allocation. Le déclenchement du
telle qu’elle est décrite dans les documents référents. La réalité des ramasse-miettes intervient lorsque la mémoire des objets est pleine
machines Java est tout autre et plus complexe, due notamment ou de manière incrémentale, selon l’algorithme du récupérateur de
aux optimisations effectuées sur ces machines. mémoire. Un récupérateur séquentiel interrompt le programme uti-
lisateur de la durée nécessaire au compactage. Un récupérateur
concurrent agit en même temps que l’exécution du programme uti-
lisateur. Le déclenchement intervient en temps partagé ou en paral-
2.3 Chargeur de classes lèle sur un coprocesseur chargé de la mémoire des objets.
Les programmes Java sont structurés en classes. Une classe Il existe plusieurs algorithmes de récupérateur de mémoire.
Java engendre un fichier compilé chargé dynamiquement au fur et Citons trois algorithmes classiques de ramasse-miettes avec des
à mesure des besoins. À la première création d’une instance d’une compteurs de référence (reference counting ), par marquage
classe, le chargeur de classes recherche le fichier localement ou (mark-and-sweep ) et par copie (stop and copy ).
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Source Java
Classe
Champs d’instance de object
Object obj = new object (); [Link]
super
Classe
Point
Point p = new Point (); Champs d’instance de
[Link]
+ champs d’instance de
Point
Classe
Champs d’instance de object
[Link]
super
obj = p;
Classe
Champs d’instance de Point
Parution : août 2013 - Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
[Link]
+ champs d’instance de
Point
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
L’appel de Java depuis C nécessite de disposer d’un ensemble Plusieurs champs sont de longueur variable, comme le champ
de primitives d’accès à la machine virtuelle. Ces primitives methods dont la taille, définie par method_count, correspond au
permettent de créer une machine virtuelle, de charger le code des nombre de méthodes de la classe Java. Le champ interfaces
classes, de créer des instances d’objets, d’accéder aux variables et recense les interfaces implémentées par cette classe.
aux méthodes. Ces fonctions utilisent des types de données
Nota : pour la définition de chaque champ, se reporter en [2].
compatibles ou confondus avec les types de la machine Java. Le
programmeur C peut également engendrer des threads, bloquer le
ramasse-miettes, adresser l’espace mémoire de la machine. Tou-
tes ces fonctions et les définitions de types font partie de l’implé- 3.2 Compatibilité binaire
mentation d’une machine virtuelle.
Aucun choix d’implantation n’est effectué lors de la génération
du fichier .class. Une variable d’instance est décrite par son nom et
non par un déplacement dans une zone mémoire. Une méthode
est désignée par son en-tête et sa signature en chaîne de caractè-
3. Classes Java res et non par une adresse de code.
Le choix de conserver une forme symbolique du fichier compilé
est la conséquence des exigences de compatibilité binaire de Java.
Cette compatibilité est préservée lors des changements de code
Chaque fichier contient un type Java (une classe, une inter- source Java décrits dans le chapitre 13 de [1], les modifications de
face ou une énumération). Un fichier est généré pour une code source les plus significatives sont :
classe interne, locale ou anonyme. Ce fichier contient le code et – l’ajout de nouveaux champs, méthodes ou constructeurs à une
toutes les données nécessaires à l’exécution des instructions classe ou à une interface ;
de la machine virtuelle. Ces données sont pour la plupart dans – le retrait de champs, méthodes ou constructeurs privés d’une
leur forme symbolique. Le format choisi du fichier permet de classe ou d’une interface ;
respecter les contraintes de compatibilité binaire et d’autoriser – le réordonnancement de champs, méthodes ou constructeurs
de futures extensions. Les principales étapes du chargeur de dans une classe existante ;
classes locales ou distantes sont également présentées. – l’ajout d’une nouvelle classe ou interface dans la hiérarchie
d’une classe.
Ce choix de forme symbolique induit :
3.1 Format du fichier compilé – la possibilité de décompilation d’un fichier .class ;
– la résolution des liens à la responsabilité du chargeur de classes.
Parution : août 2013 - Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Deux octets lus forment un mot de 16 bits, l’octet de poids fort étant Le but d’un décompilateur Java est de convertir un fichier
en premier. Les mots de 32 et 64 bits sont construits de la même .class en un code source Java.
façon. Les noms de types u1, u2 et u4 choisis par SUN [2] représen-
tent respectivement les données sur un, deux et quatre octets. Les
composantes du fichier .class sont dans l’ordre d’apparition [magic, La forme symbolique du fichier compilé permet une telle
minor_version, major_version, constant_pool, access_flags, this_class, conversion. Plusieurs outils existent, Mocha [2] semble le plus
super_class, interface, fields, methods, attributes]. connu. L’objectif est avouable lorsqu’il s’agit de reverse-enginee-
ring, comme par exemple une mise en conformité des fichiers
Ces champs sont rassemblés dans une structure nommée compilés et des codes sources Java. L’objectif peut également être
ClassFile définie ci-dessous dans une syntaxe apparentée au inavouable : dans ce cas, les fichiers compilés peuvent être
langage Java. cryptés. Un tel fichier décompilé devient en principe illisible :
– les identificateurs de classes et de variables sont remplacés
par des numéros ;
structure ClassFile { – certains champs du fichier compilé sont supprimés ;
u4 magic; – tout est fait afin de décourager un éventuel lecteur d’un fichier
u2 minor_version; .class reconstitué.
u2 major_version; Les outils Crema ou Proguard permettent de crypter un fichier
u2 constant_pool_count; .class.
info constant_pool [constant_pool_count];
u2 access_flags; Exemple
u2 this_class; Classe Point : décompilation produite par l’utilitaire Mocha
u2 super_class; / * Decompiled by Mocha from [Link] */
u2 interfaces_count; / * Originally compiled from [Link] */
u2 interfaces [interfaces_count]; public synchronized class Point {
u2 fields_count; private static int compte = 0;
field_info fields [fields_count]; private int x; private int y;
u2 method_count; public Point () {this(0, 0); }
method_info methods [method_count]; public Point (int i, int j) {moveTo (i, j); compte=compte+1; }
u2 attributes_count; public int distOrigin () {return (int) [Link] ( (double) (x * x + y * y)); }
attribute_info attributes [attributes_count]; public void moveTo (int i, int j) {x = i ; y = j; }
} }
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
À l’exception des identificateurs des paramètres de méthodes et Passe 3 : cette étape assure la cohérence du code généré.
des commentaires, le code source décompilé est ici très proche du Chaque séquence de code de chaque méthode est vérifiée. Une
code source initial. analyse du flot d’instructions est effectuée. Les instructions et leurs
opérandes sont extraits du code. Le format de chaque instruction
extraite est validé. Toutes les adresses de sauts des instructions de
3.4 Chargement et édition des liens contrôles doivent être des adresses d’instructions. Toutes les
opérations sur la pile doivent être cohérentes : « dépiler un entier »
La machine Java charge dynamiquement les classes compilées, (istore) doit être précédé d’une opération de type « empiler une
réalise les éditions des liens et exécute les initialisations de la ou valeur de type entier » (iload ou instruction équivalente). Toutes
des classes chargées. les références au champ constant_pool sont vérifiées : l’opérande
Le processus de chargement consiste à rechercher le fichier de l’instruction ldc1 doit référencer un entier et l’opérande de
compilé et à créer une représentation interne de cette classe. Les l’instruction getfield doit indiquer une entrée appropriée. Chaque
classes héritées sont également chargées et les autres classes gestionnaire d’exceptions est validé : les adresses mentionnées
mentionnées dans ce fichier sont chargées au moment de leur sont valides en désignant une adresse d’instruction.
utilisation, au fur et à mesure, lors de la première création d’une Passe 4 : cette passe assure également la cohérence des instruc-
instance par exemple. La recherche du fichier est locale ou tions utilisant d’autres classes que la classe analysée. Une affecta-
distante. Le fichier peut être extrait d’une archive (zip, jar, cab ou tion d’une instance par un objet d’une classe dérivée est validée
autres). Le chargeur peut demander la compilation d’un code quand la classe désignée est chargée, et la vérification de son
source Java afin de produire le fichier .class. Le chargement d’une appartenance au graphe d’héritage effectuée. Cette passe vérifie la
librairie en code natif est à la charge du programmeur. présence des méthodes appelées et des champs désignés par les
L’édition de lien permet d’intégrer la classe chargée au sein du instructions de cette classe, l’absence indiquant que les règles de
chargeur de classe concerné de la machine. Cette étape est compatibilité binaire ne sont pas respectées. Les types des instruc-
constituée d’une vérification, d’une préparation et éventuellement tions de manipulation des champs sont comparés aux signatures
d’une résolution. La vérification assure que le fichier est bien déclarées et présentes dans le champ constant_pool. À la fin de
formé. La préparation consiste en l’allocation des variables de cette passe, certaines implémentations de chargeur peuvent
classes. La résolution dans certaines implémentations convertit les effectuer des substitutions dans le flot des instructions et les
références symboliques en références internes de la machine. instructions avec des opérandes symboliques peuvent être
substituées par des opérandes destinés à la machine.
À la suite de l’édition des liens, l’initialisation de la classe est
effectuée. Elle consiste à exécuter le bloc statique de la classe, les Cette phase de vérification permet à la machine de s’affranchir de
instructions de ce bloc sont regroupées au sein de la méthode l’exécution des tests des limites de la pile, du contrôle des types des
interne nommée <clinit>( ). Cette méthode n’est exécutée qu’une arguments des instructions et de la validité des accès en mémoire
Parution : août 2013 - Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
seule fois au chargement de la classe, elle contient en général les locale. Cependant, cette étape de vérification peut s’avérer coûteuse
initialisations des variables de classe. en temps d’exécution. Elle est difficile à valider. La découverte
récente d’un problème de sécurité en Java 2 [3] démontre, si cela
Exemple était nécessaire, que cette phase reste perfectible.
Classe Point : le chargement de cette classe induit le chargement
de la classe héritée [Link]. Si celle-ci n’est pas présente
en machine, la classe [Link] est chargée à la première
occurrence de l’appel de la méthode distOrigin. La classe Point a un 4. Jeu d’instructions/source
bloc d’initialisation de la variable de classe.
Java
3.5 Vérification du fichier compilé
La machine Java comporte 225 instructions. L’approche
retenue ici est de présenter un sommaire des familles
La première étape de l’édition des liens est la phase de d’instructions accompagné de deux exemples représentatifs
vérification. Cette phase contribue à la notion de sécurité (§ 4.1). Ensuite, les différentes familles d’instructions sont
prônée et effective par les concepteurs du langage. Cette phase proposées de manière ascendante et progressive. Après le
de vérification se déroule en quatre passes. Elle est constituée détail du format des instructions Java (§ 4.2), cette présenta-
d’une analyse syntaxique (passe 1), d’une vérification séman- tion débute par les instructions nécessaires aux évaluations
tique (passes 2 et 3), et de la vérification de la compatibilité d’expressions arithmétiques (§ 4.3) et se poursuit par les
binaire (passe 4). instructions de contrôle (§ 4.8), les instructions d’invocation
de méthodes de classes (§ 4.11) et d’instance (§ 4.12), et enfin
les instructions de mise en œuvre de l’héritage (§ 4.14).
Passe 1 : cette étape vérifie que le fichier compilé est au bon
format. Les premiers octets doivent correspondre au nombre
« magique ». Tous les attributs ont la bonne longueur et le fichier
n’est pas tronqué et ne possède pas d’octets supplémentaires. Le 4.1 Exemples
champ constant_pool ne doit pas contenir des valeurs d’étiquettes
Les familles d’instructions peuvent se scinder en deux groupes :
non répertoriées.
– les familles d’instructions nécessaires à la gestion des
Passe 2 : cette étape correspond à une vérification sémantique des variables locales et de classes, de la pile d’évaluation, du contrôle
champs du fichier compilé, à l’exception du code vérifié dans la et les méthodes de classe ;
passe suivante. Les classes et les méthodes dénotées final ne sont – les familles d’instructions liées à la gestion des objets.
pas dérivées. Chaque classe possède une classe héritée, sauf
[Link]. La cohérence du champ constant_pool est validée, Les familles d’instructions non liées aux objets sont :
chaque entrée est testée. Une entrée référencée comme un type – arithmétiques et logiques (§ 4.3) iadd, imul... ;
CONSTANT_Class_info doit désigner pour le champ name_index une – manipulation de la pile (§ 4.4) dup, pop... ;
entrée de type CONSTANT_NameAndType_info. Toutes les signatu- – gestion des variables locales et des constantes (§ 4.5) aload,
res des champs et paramètres sont vérifiées syntaxiquement. iload, istore, iconst... ;
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
est représentée figure 10. mètre de la classe Exemple2 ainsi que la méthode de classe main.
Nota : toutes ces instructions sont détaillées dans la suite du paragraphe 4.
La création d’une instance de la classe Exemple2 engendre la
réservation de la place en mémoire des objets par l’exécution de
l’instruction new. Les appels des constructeurs de la classe et des
classes héritées sont réalisés par l’instruction invokesuper. Sur cet
exemple, la classe héritée par Exemple2 est la classe
Source Java Instructions générées
java/lang/Object. Ce constructeur alloue une nouvelle instance de la
classe Nœud et déclenche la méthode suivant, réalisée par l’instruc-
public class Exemple1{
tion invoke_virtual. La lecture de la donnée d’instance debut utilise
public static void main (String [ ] args) { l’instruction getfield.
int operande = [Link] (args [0]) ; aload_0, iconst_0, aaload
La création d’une instance de la classe Nœud engendre l’appel
invoke_static, « parseInt » des constructeurs de la classe héritée puis du constructeur par
istore I défaut de la classe Nœud. Notons l’absence de constructeur dans
la source Java et la présence normale de celui-ci dans le code
// voir figure 10 généré. L’affectation de la donnée d’instance suivante utilise
int y = f (operande) ; iload_I l’instruction putfield.
invoke_static, « f », istore_2
La pile des segments d’activation et la mémoire des objets,
obtenues après l’allocation de l’instance Exemple2 dans la
} méthode de classe main sont représentées figure 12.
static int f (int n) {
int compte = 1 ; iconst_1, istore_1 4.2 Notation et format des instructions
int resultat = 1 ; iconst_1, istore_2
La notation est xx, yy pour les opérandes : xx est l’octet de poids
// voir figure 10 fort, yy de poids faible. La valeur des octets est donnée en
[Link] décimal. Le mot constant_pool est conservé. La description des
opérations engendrées utilise les actions : empiler, dépiler et
while (compte < n) { début : sommet sur la pile d’évaluation. Des variables intermédiaires sont
compte++ ; iinc, 1, 1, également utilisées. La description de ces opérations ne fournit
que des éléments d’implémentation des instructions de la
resultat = resultat * compte ; iload_2, iload_1, imul, istore_2 machine.
} test : iload_1, iload_2, Nota : pour en savoir plus, une description plus complète des instructions est donnée
if_icmplt, début dans les ouvrages [2] [4].
return resultat ; iload_2, ireturn Les zones mémoires ainsi que les registres de la machine
conservent les littéraux définis par SUN présentés au
Figure 9 – Variables locales et méthodes statiques paragraphe 2.
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Le format d’une instruction consiste en un code opération Tableau 2 – Instructions dédiées aux expressions
spécifiant l’opération à exécuter suivi de 0 à plusieurs arithmétiques en fonction du type des opérandes
opérandes représentant les paramètres ou les données de Opéra-
l’instruction. Le code opération d’une instruction est un octet byte short int long float double
teurs Java
et l’opérande une séquence d’octets, les octets de poids forts
étant en tête de la séquence. La mémoire de code est une bipush sipush iconst lconst fconst dconst constante
mémoire d’octets et seules deux instructions nécessitent un numérique
alignement en mots de 32 bits.
iadd ladd fadd dadd +
isub Isub fsub dsub –
Exemple
imul lmul fmul dmul *
Mémoire de code {03,17,10,01} :
03 est le code opération de l’instruction iconst_0. idiv ldiv fdiv ddiv /
17 représente le code opération de sipush. irem lrem frem drem %
L’opérande est constitué de deux octets représentant un mot de ineg lneg fneg dneg – (unaire)
16 bits dont la valeur entière est (10 * 256) + 01 ; 10 est la valeur de
l’octet de poids fort et 01 celle de l’octet de poids faible. ishl lshl <<
ishr lshr >>
4.3 Évaluationd’expressionsarithmétiques iushr lushr >>>
L’évaluation d’une expression arithmétique utilise une pile iand land &
d’opérandes. Les instructions référencent de manière implicite ior lor |
cette pile : accès au sommet lors d’une opération unaire et égale-
ment au sous-sommet lors d’une opération binaire, le ixor lxor ^
sous-sommet étant alors le premier opérande.
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
L’écriture utilise l’instruction istore, cette instruction affecte la double d2i d2l d2f
variable locale par le sommet de pile. Le déplacement par rapport
au registre vars est obtenu de la même façon.
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
4.8 Instructions de contrôles compilateur. La table est alors remplacée par une liste ordonnée de
couples <clé, adresse de code>. La sélection du code s’effectue en
Les instructions de branchement ont toutes leur code opération comparant séquentiellement la variable de sélection avec la clé de
sur un octet et sont suivies d’un opérande sur deux octets repré- chaque couple afin de déterminer la séquence de code à exécuter.
sentant le déplacement relatif à ajouter au compteur ordinal (pc), si Les deux exemples suivants présentent ces deux instructions.
le saut est effectif. Les opérateurs relationnels de Java ont leurs Notons que ces deux instructions ont leur table interne alignée sur
correspondants en instruction de la machine (tableau 6). Le test un mot de 32 bits, le générateur de code assure cet alignement.
d’égalité entre deux entiers est effectué par l’instruction if_icmpeq.
Sur cet exemple, l’instruction en ligne 12, if_icmplt, effectue le saut Exemple 5 : instruction tableswitch, table d’indirections de
si le premier entier empilé est inférieur au second. Les opérandes taille 2 (figure 15).
ont comme valeur 255,249 soit la valeur – 7. L’adresse de débran-
chement est en 5 (adresse 12-7). La table contient pour i = 0 l’adresse 24, et pour i = 1 l’adresse 29,
elles représentent les adresses de code respectives.
Exemple 4 : itération et incrémentation (figure 14).
Exemple 6 : instruction lookupswitch, liste ordonnée de taille 2
(figure 16)
4.9 Instructions pour la sélection
La liste contient les couples <0,32> et <1000,27>, soit en absolu
(switch/case ) les couples <0,69>,<1000,64> (69 pour 37 + 32).
Les instructions tableswitch et lookupswitch implantent
l’instruction Java switch/case. L’instruction tableswitch est suivie
d’une table dans laquelle est recensée, pour chaque valeur possible 4.10 Variables de classes
de l’expression testée, l’adresse de code correspondant. La taille de
cette table est liée aux valeurs des constantes possibles pour Les variables de classes sont assimilables aux variables globales
l’expression de sélection installée par le programmeur. Cette taille d’un programme. L’instruction getstatic place en pile d’évaluation
est égale à la valeur de la constante maximale soustraite de la la valeur d’une variable de classe et l’instruction putstatic range le
constante ayant la valeur minimale. La valeur de l’expression sommet de pile dans la zone des variables associées à chaque
devient l’indice dans cette table auquel se trouve l’adresse de code à classe. Les opérandes de ces instructions sur deux octets
exécuter. Si cette table est trop importante, ou les valeurs de sélec- représentent l’indice dans le champ constant_pool. À cet indice se
tion trop éparses, l’instruction lookupswitch est préférée par le trouve la désignation symbolique complète du champ de classe.
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
4.11 Appel de méthodes de classes cite d’une classe Java, ce bloc est exécuté une seule fois au
chargement de la classe. Les retours de fonction s’exécutent à
L’instruction invokestatic réalise l’appel d’une méthode de l’aide d’instructions liées aux types retournés (tableau 7).
classe, l’opérande sur deux octets indiquant l’entrée dans le
champ constant_pool à laquelle se trouve la désignation de la
méthode appelée. La recopie des arguments dans la zone frame Tableau 7 – Instructions de retour de fonction
indicée par le registre vars est à la charge de la machine. Le
segment d’activation contient l’adresse de retour, l’adresse du int long float double reference
segment de l’appelant ainsi que la zone des variables locales,
paramètres inclus. Notons que la méthode avec l’identificateur
ireturn lreturn freturn dreturn areturn
interne <clinit>()V est associée au bloc statique implicite ou expli-
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
ireturn} restaurer ;
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
interface Mobile {
public void moveTo (int nx, int ny) ;
}
public class PointM implements Mobile {
private int x, y ;
public void moveTo (int nx, int ny) {x = nx ; y = ny ; }
public static void m ( ) {
Mobile m = new PointM ( ) ;
aload_0 [Link] (10, 10) ; empiler (m) ;
bipush, 10 empiler (10) ;
bipush, 10 empiler (10) ;
invokeinterface, 00, 06, 03, 00 [Link] ( ) ; / / (II) V
return
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
l’instruction ont une taille de trois octets, l’adresse de retour (pc + 3) fiabilité de Java et de son interprétation. Le principe est d’effectuer
est empilée. La première instruction du sous-programme sauve le la vérification une seule fois, à la première occurrence de l’instruc-
sommet de pile en mémoire locale. L’instruction ret correspond au tion. Celle-ci est alors remplacée par sa version optimisée avec le
retour de sous-programme ; elle utilise cette sauvegarde en mémoire suffixe _quick ; les opérandes ont alors une autre signification. Ce
locale afin de poursuivre l’exécution. type d’optimisation concerne les instructions de lecture des
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
constantes, de manipulation des champs d’instances, d’invocation Exemple 12 : table des méthodes virtuelles (figure 22)
des méthodes de classes ou d’instance et d’allocation des objets.
Cette optimisation consiste à générer pour chaque classe une table
Cela représente vingt-cinq instructions liées principalement à la
manipulation des objets (tableau 9). En développement orienté des méthodes appelée communément table des méthodes
objet, l’impact de ces optimisations en gain de performance est virtuelles (TMV). Le principe est de choisir pour les méthodes mas-
important. Les quelques mesures effectuées [5] le confirment. quées ou surchargées les mêmes indices dans les différentes tables
de chaque classe du même graphe d’héritage. Une nouvelle méthode
est ajoutée à la table avec un nouvel indice. Le code est modifié :
Exemple : lecture de constante l’instruction invokevirtual_quick remplace invokevirtual et les deux
L’optimisation pour ce type d’instructions consiste à générer une nouveaux opérandes représentent l’indice dans la table. Le coût de la
nouvelle table dans laquelle les constantes extraites du champ liaison dynamique est alors le coût d’une seule indirection. Rappelons
constant_pool sont placées. L’opérande de l’instruction devient que le choix de la méthode est effectué en fonction du type constaté
l’indice dans cette table. de l’objet à l’exécution et ne peut être résolu statiquement.
ldc,00,03 : les opérandes désignent l’entrée 3 du champ Ajoutons à la classe Point la méthode public String toString() ;
constant_pool est remplacée par ldc1_quick, 01,__ : l’opérande désigne celle-ci masque la méthode héritée de la classe Object. On obtient le
l’élément de la table à empiler. Le deuxième octet n’est plus utilisé. même indice dans la TMV de la classe Object ainsi que la TMV de la
classe Point. L’instruction invokevirtual, 00, 03 devient
invokevirtual_quick, 00, 05, les opérations engendrées par cette
Exemple : accès aux champs d’instance instruction consistent en l’obtention de la classe constatée de
Le calcul de la taille de chaque champ et de son déplacement dans l’instance et de l’appel de la méthode en TMV.
une zone allouée pour chaque instance est effectué. Le déplacement
devient l’opérande des instructions de lecture et d’écriture des
champs d’instance. 4.19.2 Compilateurs JIT (just-in-time )
getfield,00,03 : les opérandes désignent l’entrée 3 du champ Deux possibilités se présentent pour l’exécution des instructions
constant_pool est remplacée par getfield_quick,01,__ : le premier de la machine Java sur un processeur standard : interpréter les
octet désigne le déplacement dans la zone des données de cette instructions du fichier .class ou poursuivre la phase de compilation
instance. Le deuxième octet n’est plus utilisé. du code jusqu’à l’obtention d’instructions du processeur de la
L’optimisation des instructions d’accès aux champs de classe machine hôte (code natif). Cette phase de compilation a lieu au
adopte les mêmes substitutions : getstatic est remplacée par chargement du fichier .class, on parle alors de compilation à la
getstatic_quick avec comme opérande la valeur de déplacement dans volée ou de compilation just-in-time (JIT).
la zone des variables de classe. La distinction entre les valeurs sur 32 Simuler un processeur Java par l’interprétation des instructions
bits et les valeurs sur 64 bits est effectuée par des instructions nécessite une sélection en fonction du code opération de l’instruc-
dédiées, par exemple l’instruction getstatic2_quick. tion, et en moyenne trois instructions du processeur hôte ainsi que
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
invoke_virtual_ Exemple : une machine peut ainsi fournir son état mémoire et
aload_2 141 getstatic 25 520
quick accepter de la part d’un client distant un déclenchement du
ramasse-miettes comme le montre la figure 23.
aload_3 227 putstatic 5
aaload 60 getfield 37 Une notification distante à travers le réseau à la suite d’une
anomalie par exemple est en standard avec JMX.
(1) Notons que les valeurs obtenues pour les optimisations de type_quick
sont élévées.
4.19.4 Dalvik machine sous Android
cinq références mémoires pour chaque instruction Java. En Java est le langage de programmation choisi par Google pour le
conséquence, l’interprétation est de trois à vingt fois plus lente que développement d’une application pour son système Android. Ce
l’exécution du code compilé. Néanmoins, l’espace mémoire alloué système est maintenant largement répandu sur les smart-phones
au code est plus restreint. et tablettes. Dans la chaîne de développement, le compilateur Java
génère les fichiers .class décrits dans le paragraphe 2.1. Ces
Une compilation de type JIT simple consiste à substituer chaque fichiers sont transformés en fichiers .dex et assemblés afin de
appel d’instruction Java par la séquence des opérations fournir une application Android. Ces fichiers .dex sont exécutés par
engendrées extraite de l’interpréteur. Le coût en temps dû à la une machine virtuelle nommée Dalvik Virtual Machine (DVM). La
sélection est évité, mais la taille du code augmente de façon DVM est une machine à registres avec son jeu d’instructions
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
5. Conclusion
Connection Windows En quelques années, la diffusion de Java a engendré un nombre
4064@localhost important d’implémentations de la machine virtuelle. Il devient rare
Summary Memory Theards Classes MBeans VM de disposer d’un ordinateur sans une machine virtuelle Java. Dix
Chart : Memory Pool "Tenured Gen" Perform GC
années se sont écoulées depuis la première version de cet article, et
fait assez rare en informatique, la définition du jeu d’instructions et
9.0 Mb
la grammaire des fichiers générés sont restées les mêmes. Le code
Used
6,842,352
intermédiaire assure une stabilité et les millions de machines Java
6.0 Mb Threshold installées dans le monde y contribuent également.
5.0 Mb 6,144,000
21:51 21:52 21:53 21:54 21:55 21:56 21:57 21:58 21:59 22:00 22:01 22:02 Cette machine virtuelle est maintenant présente sur bon nombre
d’ordinateurs allant du serveur d’applications pour de grandes entre-
Details
prises aux clients équipés d’un navigateur. Un autre succès a été le
Time : 2004-11-03 22;02;08 100% portage d’une version minimale de cette machine [7] sur les mobiles
Used : 6,681 kbytes
Committed : 13,088 kbytes
75% que l’on pourrait qualifier de première génération, la machine vir-
Max : 60,544 kbytes 50% tuelle développée par Google pour leur système Android en Java
Usage Threshold : 6,000 kbytes 25%
GC time :0.887 seconds seconds on Copy (402 collections) semble s’imposer sur les smart-phones et autres tablettes. Un autre
6.608 seconds seconds on MarkSweepCompact 0% succès toujours actuel est le portage de cette machine sur une carte
(62 collections)
SIM équipant la plupart des téléphones mobiles.
De nouveaux langages sont apparus : Scala, Groovy, JJRuby,
Jython, Clojure, et le point commun entre ces langages est qu’ils
utilisent la machine virtuelle Java. La dernière version du JDK
Figure 23 – Copie d’écran (doc. Oraclé Technology Network) (Java Development Kit), la JDK7 propose une nouvelle instruction
invokedynamic, générée par une version d’un compilateur pour
JRuby.
approprié. La taille du code généré est supérieure à celui généré
par une machine à pile, ce qui est largement compensée par une La pérennité de l’architecture présentée dans cet article semble
optimisation du fichier .class devenu .dex. assurée au moins pour la prochaine décennie.
Parution : août 2013 - Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
P
O
U
Machine virtuelle Java (JVM) R
E
par Jean-Michel DOUIN
Ingénieur et docteur du CNAM
N
Enseignant/chercheur au CNAM
S
Sources bibliographiques
A
[1] GOSLING (J.), JOY (B.) et STEELE (G.). – The [4] McGRAW (G.). – Java 2’s verifier becomes [Link] V
java language specification. confused by german student’s security at- [Link]
[2]
[Link]
LINDHOLM (T.) et YELLIN (Y.). – The java vir-
tack.
[Link]
[7] K Virtual Machine. White paper.
[Link]
O
tual machine specification. http:// 1999/[Link]
[3]
[Link]/docs/books/vmspec
DYER (D.). – Java decompilers compared.
[5] WILKINSON (T.). – Open source implementa-
tion of a java virtual machine.
I
[Link]
(1997).
[Link]
1997/[Link]
[6] The Java Hotspot performance engine archi-
tecture. White paper.
R
Parution : août 2013 - Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
Sites Internet
Garbage – First Collector PicoJava et processeurs Java
P
[Link]
Carte à puce Java Sun
[Link]
[Link]
Oracle Technology Network
[Link]
L
U
Annuaire
Fabricants (liste non exhaustive) Oracle
S
[Link]
Dallas Semiconductor
[Link] Organismes
Ajile Systems Standard Performance Evaluation Corporation (SPEC)
[Link] [Link]
tiwekacontentpdf_h1588 v2 Ce document a ete delivre pour le compte de 7200031372 - nantes universite // [Link]
L’EXPERTISE TECHNIQUE
& SCIENTIFIQUE DE RÉFÉRENCE
15 DOMAINES D’EXPERTISE
[Link]