• Aucun résultat trouvé

5.2 Chargement des méta-données Java

5.2.1 Chargement de classes

Le chargement de classes peut avoir lieu à plusieurs moments durant l'exécution de la JVM. Elle est déclenchée en premier lieu lors du démarrage de la machine virtuelle pour charger les classes de base. Puis elle intervient au lancement d'une application pour charger les classes de cette application mais aussi leurs dépendances. Enn elle peut se produire lors d'appels explicites au chargement dynamique de classes pendant l'exécution.

5.2.1.1 Acquisition des méta-données

Cette étape consiste principalement à réserver un espace mémoire pour les méta-données lues depuis le chier de classe. Cette phase du chargement commence par une pré-vérication analysant la structure et l'intégrité du chier de classe conformément au modéle détaillé g- ure 5.1. Si le chier est correct, son contenu est transformé dans le modèle de méta-données propres à la JVM.

5.2.1.2 Édition des liens

L'édition des liens est une étape de post-compilation réalisée en-ligne, alors qu'en natif cette étape est réalisée dès la compilation. Cette étape nalise la compilation hors-ligne qui a crée le chier de classe pour lier les classes entre elles et résoudre les dépendances trouvées en parcourant la table des symboles.

Comme signalé, une JVM n'a aucune garantie que la classe en cours de chargement ait été produite par un compilateur respectant les standards du langage. Dans un autre cas de gure, une classe peut avoir été produite correctement, mais une de ses dépendances, correcte à la compilation, peut avoir été modiée entre temps, brisant ainsi la compatibilité entre classes. L'édition des liens est donc une obligation et une condition sine qua none à toutes utilisations de nouvelles classes par la JVM.3

L'édition des liens comportes trois étapes, qui doivent impérativement être exécutées dans l'ordre :

Vérication L'étape de vérication s'intéresse principalement à l'intégrité du code et son innocuité. Cette étape reprend toutes les problématiques et techniques déjà présentées section 2.1.5.2, page 10.

Préparation La préparation est une étape de pré-initialisation allouant l'espace mé- moire pour les champs statiques et aectant les valeurs constantes. À la diérence de l'initialisation, qui intervient plus tard, aucun code Java n'est pour l'instant exécuté. Résolution L'étape de résolution, plus ou moins optionnelle, consiste à déterminer les valeurs propres à la JVM de tout ou partie des références symboliques du ConstantPool. Cette étape peut ainsi être l'occasion d'une nouvelle transformation appliquée au modèle de méta-données interne à la JVM. Par exemple un symbole résolu peut ainsi remplacer

3À noter, que chaque étape de l'édition des liens peut alors être initiatrice du chargement d'autres classes,

correspondant à des dépendances n'ayant pas encore été chargées. Le succès du chargement de la classe en cours est alors conditionné par le succès du chargement de ses dépendances.

un ou plusieurs champs du modèle, voire créer de nouvelles structures comme par exemple une table de méthodes virtuelles, une nouvelle version réarrangée du ConstantPool (i.e. jugée mieux adaptée à l'exécution par le concepteur), un ConstantPool commun à tout le système, etc.

Notons enn que la résolution peut très bien intervenir tardivement, c'est-à-dire unique- ment au moment où cela est nécessaire, voire même sans aucune mémorisation de la réso- lution et donc avec une résolution rééditée à chaque fois que cela est nécessaire.

5.2.1.3 Initialisation

L'initialisation consiste à exécuter l'initialiseur statique Java de la classe, si il est présent. L'initialisation statique se fait au moment opportun, c'est-à-dire au libre de choix de la JVM, mais une seule fois, et sur toute la hiérarchie de classes dont la classe courante est la plus basse et en commençant par l'ancêtre non-initialisé le plus éloigné.

Un champ statique d'une classe étant un objet Java unique, l'initialisation est une con- dition préalable à toutes utilisations de la classe en cours de chargement, qu'il s'agisse de création d'une nouvelle instance, d'un accès à un de ses champs statiques ou une de ses méthodes statiques. La classe n'est considérée chargée et instanciable que lorsque cette dernière étape a eu lieu.

5.2.2 Pré-chargement de classes

Dans les architectures de machines virtuelles scindées (SVM), classiques dans les systèmes embarqués de petites tailles, le chargement de classes est eectué sur une station de travail, en dehors de l'environnement d'exécution de la JVM (i.e. une carte à puce, un système embarqué communiquant,...) et donc en dehors de la VM même malgré l'appellation de SVM. Cette approche est celle utilisée par JavaCard, et est une des options suivies par plusieurs JVM embarquées comme KVM [Simon 1999], Squawk [Simon 2006], Darjeeling [Brouwers 2009] ou JITS [Courbot 2010] pour ne citer qu'elles.

Ces technologies utilisent deux approches possibles oertes par une SVM. Soit l'utilisation d'un format de chier dédié et pré-chargé comme le chier CAP JavaCard, ou alors une approche dite par Romisation ou chargement précoce.

5.2.2.1 Chargement précoce

La Romisation est une technique qui consiste à charger à l'avance des méta-données di- rectement sous la forme qu'elles prendront à l'exécution dans la JVM cible. Cette image, ou  Rom  , est une super-structure C générée hors-ligne par un outil spécialement conçu pour et fonctionnant comme un chargeur de classe. Une instance de cette super-structure C alimentée statiquement par les données des chiers de classes est ensuite compilée di- rectement dans le binaire de la JVM.

La Romisation est donc un pré-chargement global de classes où toutes les méta-données du futur système sont rassemblées dans un seul conteneur. Elle ore ainsi l'avantage de déporter un processus coûteux hors du système embarqué cible. La Romisation n'empêche par l'ajout de nouvelles classes sur le système cible post-déploiement mais bien souvent l'image a pour but d'être placée dans une mémoire non-inscriptible. Si tel est le cas, la mécanique de chargement de classes peut alors elle aussi être supprimée de la JVM cible et libérer ainsi beaucoup d'espace. Car dans ces conditions, il n'est plus besoin de revérier de code à chaque re-démarrage puisqu'il n'est plus jamais modiable.

La Romisation est aussi souvent l'occasion de certaines optimisations dont la principale est le compactage du ConstantPool. L'idée directrice est de le rendre commun à toutes

les classes de la Rom, éliminant ainsi les redondances de chaines de caractère et libérant énormément de place dans le système cible.

De plus, dans une image romisée, puisque toutes les classes du système sont présentes, la résolution des symboles et l'éditions des liens entre classes peuvent être poussées à une phase très avancée. Dès sa fabrication, il devient aussi possible de spécialiser la Rom par la suppression des classes, méthodes ou chaînes de caractères qui ne seront jamais utilisées [Courbot 2010].

5.2.2.2 Format pré-chargé

Un format de chier dit pré-chargé quant à lui annule et remplace le chier de classe au format standard. Généralement en prenant plusieurs classes à la fois, mais jamais l'ensemble complet des classes du système comme avec la Romisation. Le chier pré-chargé est donc un morceau de Rom avec les mêmes propriétés et possibilités que cette dernière. Le chier CAP de JavaCard par exemple est la Romisation d'un paquetage Java complet.

Les classes de ce paquetage sont donc chargées hors-ligne et leurs méta-données agencées dans un format permettant leur exécution directe par la JVM JavaCard. Le mode de génération de ce type de chier est la conversion, mode présenté section 2.2.2.2, page 17.

L'avantage du format pré-chargé par rapport à la Romisation est qu'il permet l'ajout d'applications post-déploiement de la JVM et du système cible sans réel chargeur de classe. Le chier étant déjà pré-chargé, la JVM n'a pas besoin d'eectuer toutes les étapes décrites précédemment. La conversion prend en eet en charge la résolution symbolique à l'intérieur d'un paquetage Java et prépare déjà l'initialisation des champs statiques.

Toutefois, un certain nombre d'étapes doivent quand même être réalisées en ligne comme la re-vérication du code et les liaisons inter-paquetages. Bien que ces dernières soient simpliées par la création du chier d'export (voir une nouvelle fois section 2.2.2.2).

5.2.3 Synthèse

Contrairement au code compilé, le problème des méta-données n'est plus un problème d'adressabilité. Néanmoins, stocker des méta-données comme celles de Java dans une mé- moire non-adressable et imaginer les aborder comme du code ou des données classiques est impossible.

• d'abord, simplement parce que leurs formats sont diérents ;

• ensuite parce que leurs motifs d'accès, fait de plusieurs indirections, tout en étant immergées dans le ot d'exécution, forcent la VM vers beaucoup plus de lectures aléatoires et surtout brise la propriété de séquentialité d'un bloc de base ;

• et enn parce que le modèle de données guidant ces motifs d'accès est dépendant de constructions propres à chaque implémentation de JVM.

Pour évaluer ces diérences, nous proposons maintenant une présentation de deux MMD, en commençant par celui de la Rom KVM, qui sera suivi d'une présentation du MMD pré- chargé JavaCard, le chier CAP.

5.3 Modéle KVM

Le modèle de méta-donnée de KVM décrit dans la gure 5.3 est relativement proche de celui-ci édicté par les spécications de la JVM standard, page 76. Il utilise par exemple le même schéma par tableaux pour associer méthodes et champs à une classe.

Cependant, le modèle de KVM utilise d'autres champs de méta-données qui lui sont propres. Un exemple est le champ instSize de instanceClassStruct qui résulte du pré-calcul du nombre de champs4 qu'un objet instanciant une classe possédera. Ce pré-calcul permet d'allouer rapidement l'espace nécessaire au stockage de cet objet dans le tas de la JVM. Le même mécanisme est utilisé dans methodStruct pour le champ frameSize qui permet d'allouer rapidement sur la pile l'espace nécessaire pour l'exécution d'une méthode.

KVM supportant les processus légers et la synchronisation, le modèle de donnée le supporte donc aussi. La méta-donnée classStruct de KVM est ainsi également une instance de la classe java.lang.Class synchronisable, ce qui alourdit le modèle.

Figure 5.3: Modèle des méta-données de KVM