Architecture de la jvm(1ere Partie) - JVM Architecture (First Part)
1. Nousallonsconsacrerune série d’articlessurl’environnementd’exécutionde javaqui estla
machine virtuelle de java.
En tant que développeurjava,il estparfoisnécessaire de savoirce qui se passe dans cette
machine abstraite qu’estlaJVM(JavaVirtuel Machine) lorsque nousécrivonsnotre code java.
Ceci nousfacilite lamaintenance,larésolutiondesdifférentsproblèmes,lacompréhensiondes
exceptionsqui arriventlorsde l’exécutionde nosprogrammesjava.Outre cetaspect,il ya aussi
l’écriture duboncode.
Nosarticlesvontportersur :
L’architecture de laJVM.
Compréhensionde lamémoire enJava.
La Structure d’une classe compilée
Desdifférentes étapesdanslaJVMde l’écriture ducode source à l’exécution.
Le Ramasse Miette (Garbage Collector)
Calibrage de laJVMselonlescontraintesetlesbesoins.
L’architecture de la JVM
La JVM est une machine abstraite, dans laquelle s’exécute du bytecode, c’est du code
intermédiaire entre le langage machine binaire et le code source, il est généré après
compilationducode source parle compilateurJAVAC.Ondistingue différentesimplémentations
de JVM, notamment Hotspot, JRockit, IBM, celle utilisée dans les environnements Android,
Dalvik et plusieurs autres. Il faut distinguer trois aspects quand on parle de la JVM, il y a d’un
côté la spécification et l’implémentation et de l’autre l’instance d’exécution. Dans notre
développement nouspourronsfaire mention des trois aspects. Nos articles seront consacrés à
HotSpot qui est une implémentation Oracle.
I-Responsabilité de la JVM.
Dans le processus d’exécution des programmes java, la JVMa pour responsabilité de garantir,
un environnement sécure et une certaine abstraction au niveau de la couche système. Le
leitmotiv des programmes Java quand ce langage fut créé en 1994, c’était Write Once Run
EveryWhere qui veut dire après l’écriture de votre code source java, vous pouvez l’exécuter
dans n’importe quelenvironnement.Pourceux qui ont programmé enCou enC++ saventque la
2. compilation et l’exécution des programmes écrits dans ces langages sont dépendants des
environnements d’exécution.
Dans la responsabilitéde laJVM,celle-ci garantiele chargement du code compilé (bytecode) ,
la vérificationde l’intégrité ducode chargé,l’assurance que celle-ci respecte bienlasémantique
du langage java, l’allocation de mémoire, l’initialisation des variables, la résolution des liens
symboliques et l’exécution du code chargé.
2-Comment la JVM garantie l’exécution de ces différents contrats?
2-1-Chargement des Classes
La JVM contient des chargeurs de classe qui lui permettent de charger celle-ci pour des fins
d’exécution(Figure 1). Ils(chargeurs) s’occupentde mettre àladispositionde laJVM,lesclasses
de l’API Java et vos classes nécessaires à l’exécution de votre programme.
3. Figure 1
On distingue 2typesde chargeursde classe:
Le chargeur primordial encore appelé Bootstrap, qui est écrit en code natif (langage c)
et parent de tous les chargeurs. Son rôle est le chargement du noyau de java(JDK)
localisé dans ce jar : rt.jar
Les chargeurs définis par l’utilisateur (User-defined ClassLoader) écrits en java par
l’utilisateur et compilés en bytecode.
La JVMfournit3 chargeurs de classe par défaut : Bootstrap, Extensionet Systemappelé encore
chargeurde classe d’application.
4. Le fonctionnementdeschargeursde classe sontbaséssur3 principesfondamentaux :
La délégation
La visibilité
L’unicité
Outre ces principes,leschargeurs fonctionnentde manière hiérarchique (voirFigure 2) et
respectentune architecture baséesurl’espace de nom(namespace).Nous yreviendronsdans
cet article.
Délégation
Dans le principe de délégation,le chargeur de classe délègue le chargement d’une classe à son
parent et se substitue à celui-ci s’il ne parvient pas à trouver ou à charger la dite classe.
Visibilité
Dans le principe de visibilité,le chargeur fils à la possibilité de voir les activités de chargement
de son parent, en revanche, l’inverse n’est pas possible.
Unicité
Dans le principe d’unicité,touteslesclassessontchargéesune seulefois selon l’espace de nom
du chargeur.La délégationpermetàchaque chargeurde vérifierle principed’unicité, et grâce à
la visibilité, le fils peut voir les activités de chargement de son parent.
5. Figure 2
Class.forname(),ClassLoader.loadClass(),l’API de réflexion, et JNI_FindClass sont les éléments
de l’API de Java qui peuvent initier le chargement d’une classe ou d’une interface. Le
chargement d’une classe nécessite au préalable le chargement de toutes les super classes
(classeshéritées)et toutes les superinterfaces (interfaces héritées). Il est à noter ici quand on
parle de chargement, il est question aussi d’interfaces implémentées par les classes.
Pendant la phase de chargement d’une classe ou d’une interface, la machine virtuelle a la
responsabilité de :
6. Produire une Stream du code binaire de la classe ou l’interface à charger.
Parser la Stream en question, puis extraire les métas données de la classe ou de
l’interface encoursde chargement etl’enregistrerdanslesstructures appropriées dans
la zone de mémoire prévue à cet effet (Method Area).
Créer une instance de java.lang.Class dans le Heap qui (java.lang.Class) représente le
type de toutes les instances de cette classe.
Des erreurspeuventmodifierle fluxd’exécutionpendant la phase de chargement,
notamment :
NoClassDefFoundError qui intervient lorsque la représentation binaire de la classe ou
de l’interface à charger ne peut être trouvée
ClassFormatError qui intervient quand le format de la classe ou l’interface chargée ne
respecte pas les spécifications
UnsuportedClassVersionErrorintervientégalementpendantlavérification duformatde
la syntaxe de la classe, si celui-ci ne respecte pas les spécifications.
ClasscircularityError intervient si la classe a un problème avec sa hiérarchie, au niveau
de l’héritage,notamment si laclasse estelle-même sa superclasse ou l’interface de sa
superinterface.
IncompatibleClassChangeError intervient si le parent de l’interface n’est pas une
interface ou alors le parent d’une classe est une interface.
Comme mentionné précédemment, les classes ou les interfaces sont chargées dans le nom
d’espace de son chargeur, le nom d’espace est l’équivalent de package du point de vue de
chargement dans la JVM. Ce principe implique la possibilité de voir une classe chargée plus
d’une fois par des chargeurs différents.
L’ordinateurne s’exprimantqu’encode binaire,il abesoinpourl’exécutionducode chargé, une
traductionencode machine oucode binaire.L’élémentayantcette responsabilité est le CPU ou
le processeur.Cependant,il ne s’exprime qu’en assembleuretvulaversatilité de ce langage, dû
au faitdesconstructeursde processeur(INTEL,AMDetc…), on distingue deux typesde langages,
notamment, les langages compilés et les langages interprétés. Le C++, le C ou le Fortran sont
considérés comme des langages compilés, parce que le code source est traduit en langage
assembleurpar un compilateur statique spécifique à un processeur, puis en langage machine.
Contrairement à certains langages comme PERL ou PHP qui sont interprétés. Le code écrit en
langage interprété peuts’exécuter surn’importe quelle type de machine, tout aussi longtemps
qu’elle possèdele boninterpréteurprévuàcet effet. Ce qui se traduit par une perte de vitesse
dans l’exécutionducode interprété,parce que dansducode interprété,chaque instruction doit
être traduite par l’interpréteur, et en plus il ne peut faire qu’une instruction à la fois. Ce qui
n’est pas le cas dans le cas du code compilé. En somme les programmes écrits en langage
interprétés sontportablesmaislentsetles programmes écrits en langage compilé, ne sont pas
portables mais rapides. La JVM tire donc profit de ces deux notions grâce à son interpréteur
pour la portabilité son compilateur la JIT (Just In Time) pour la vitesse d’exécution.