next up previous contents index
Next: Algorithmes Up: Objets Previous: Flots d'instructions : les

La Machine Virtuelle Java

     

Le résultat de la compilation d'un programme Java (plus exactement, d'unités de compilation Java, c'est-à-dire les fichiers en .java) est un ensemble de fichiers de classe, un par type défini dans le programme ; le nom d'un tel fichier est formé par le nom du type (classe ou interface) suffixé par .class. Le format des fichiers de classe est indépendant de la plate-forme matérielle et logicielle : que l'on soit sur un Pentium, un PowerPC, un Sparc ou sur un Alpha, sous Windows, MacOS, Solaris ou Linux, etc. -- dans un navigateur Web, sur un Palm Pilot, voire sur une carte à puce (avec cependant un prétraitement et quelques restrictions, vu la limitation des ressources de ces cartes). Contrairement à d'autres langages (notamment C et C++), Java n'est pas compilé en langage machine : cette indépendance garantit la portabilité des applications écrites en Java, de la même façon qu'une carte SIM peut être insérée dans n'importe quel téléphone GSM.

Une application Java peut être exécutée sur une plate-forme quelconque à la seule condition que celle-ci dispose d'une Machine Virtuelle Java, ou JVM. C'est le cas d'un navigateur Web capable d'exécuter des applettes Java, ou des plates-formes comportant un environnement de développement, par exemple le Java Development Kit (JDK), ou simplement un environnement d'exécution comme le Java Runtime Environment (JRE), l'un et l'autre produits par Sun. Dans ce dernier cas, une JVM est démarrée en exécutant le programme java et en lui fournissant le nom d'une classe (et éventuellement, des arguments) :

linux% java Application

Ceci suppose que la classe Application est une classe principale, c'est-à-dire comporte la définition d'une méthode main(). L'exécution de cette commande déclenche une série d'étapes, qui sont pour l'essentiel :

Le chargement consiste à chercher un fichier de nom Application.class, à le lire, et à incorporer à la JVM une représentation binaire de la classe Application. Cette étape peut échouer si un fichier de classe contenant la définition de la classe Application n'est pas trouvé, ce qui provoque un message signalant l'erreur NoClassDefFoundError. Elle peut aussi échouer si le fichier trouvé n'a pas le format d'un fichier de classe : le message signale l'erreur ClassFormatError.

L'étape de liaison comporte trois phases. Elle commence par une vérification de la classe chargée ; en principe, cette étape serait inutile si l'utilisateur était sûr du compilateur ayant produit le fichier de classe, ce qui n'est pas le cas s'il utilise une classe dont il ignore la provenance. La vérification peut échouer et produire un message signalant l'erreur VerifyError. La deuxième phase est la préparation, qui crée les champs statiques de la classe, les initialise à leurs valeurs par défaut et met en place certaines structures de données utiles à l'exécution (par exemple, la table des méthodes).

La troisième phase est la résolution : elle remplace les références externes (à d'autres types, à leurs champs, méthodes et constructeurs) qui apparaissent dans le fichier de classe sous forme de noms par des références internes à la JVM, plus efficaces. Cette phase peut se produire juste après la vérification, ou bien être différée jusqu'à l'exécution, quand une référence externe est effectivement utilisée. Dans le premier cas, elle provoque le chargement d'autres classes, leur vérification, préparation, résolution, etc. La résolution peut échouer et produire un message signalant diverses erreurs, par exemple IllegalAccessError, InstantiationError, NoSuchFieldError, NoSuchMethodError : ces erreurs sont généralement dues à des modifications de la classe référencée faites après la compilation de la classe en cours de résolution.

L'initialisation d'une classe consiste principalement à initialiser ses champs statiques, mais elle requiert que ses surclasses aient été initialisées auparavant. Ceci induit le chargement de ses surclasses, leur vérification, etc.

Une fois ces opérations réalisées, la JVM crée un thread principal qui exécute la méthode main(). D'autres threads peuvent être créés en cours d'exécution. L'espace mémoire de la JVM est composé de plusieurs zones (voir figure § 1.22) :

Si la zone des méthodes, le tas ou la pile n'est pas assez grande et ne peut être agrandie, l'erreur OutOfMemoryError est signalée. Cette organisation est voisine des processus d'exécution des programmes Pascal, C, C++, mais pas de Fortran 77. Les implémentations classiques de Fortran 77 ne disposent ni de tas ni de pile, le processus est uniquement constitué de données statiques (de ce fait, la récursivité n'est pas permise).


Variables et objets ont une existence dans l'espace et dans le temps. Dans l'espace, c'est une zone mémoire qui a été attribuée par allocation , dans le temps c'est la durée de vie, qui s'étend de l'allocation à la dés-allocation de cette zone mémoire. La durée de vie d'une donnée est une portion de la durée d'existence d'une JVM.

  Une variable locale, c'est-à-dire résultant d'une définition à l'intérieur d'une méthode, est allouée sur la pile et a pour durée de vie celle d'un cadre d'invocation, c'est-à-dire d'une invocation jusqu'au retour de cette méthode. Si une méthode est invoquée plusieurs fois au cours de l'exécution du même programme, chaque invocation donne lieu à une nouvelle instance de chaque variable locale.

  Un objet créé par l'opérateur new est alloué sur le tas  ; il a une durée de vie qui s'étend depuis le retour de new jusqu'à une dés-allocation de cet objet qui est réalisée par le système quand l'objet cesse d'être utilisable.


 \begin{figurette}% latex2html id marker 2512
\begin{center}
\leavevmode
\fbox{...
... sont figurés), et le tas où résident les objets.}
\end{center} \end{figurette}


next up previous contents index
Next: Algorithmes Up: Objets Previous: Flots d'instructions : les
R. Lalement
2000-10-23