Comment fonctionnent les compilateurs Java AOT?


18

Il existe quelques outils ( Excelsior JET , etc.) qui prétendent transformer les applications Java en exécutables natifs ( *.exe). Cependant, je crois comprendre que ces outils ne font que créer des wrappers natifs qui invoquent / s'exécutent à javapartir d'un shell ou d'une ligne de commande.

Si cette compréhension est incorrecte, je ne vois pas comment cela pourrait être. Si une JVM ( javaprocessus) en cours d' exécution est essentiellement un interpréteur haute performance, chargeant du bytecode à partir de fichiers de classe Java à la volée, je ne vois pas comment une application Java (une collection de fichiers de bytecode qui servent d'entrée à une JVM) pourrait jamais être vraiment converti en un exécutable.

En effet, le processus JVM est déjà un exécutable natif qui prend en entrée des ensembles de fichiers de bytecode. Pour fusionner ces fichiers de bytecode et le processus JVM en un seul exécutable natif unifié ne semble pas possible sans réécrire complètement la JVM et le dé-railing de la spécification JVM.

Je demande donc: comment ces outils "transforment" réellement les fichiers de classe Java en un exécutable natif, ou le font-ils?

Réponses:


26

Tous les programmes ont un environnement d'exécution. Nous avons tendance à oublier cela, mais c'est là. Lib standard pour C qui encapsule les appels système vers le système d'exploitation. Objective-C a son runtime qui encapsule tout son message qui passe.

Avec Java, le runtime est la JVM. La plupart des implémentations Java que les gens connaissent sont similaires à la machine virtuelle Java HotSpot qui est un interpréteur de code octet et un compilateur JIT.

Cela ne doit pas être la seule implémentation. Rien ne dit que vous ne pouvez pas créer un runtime lib-esque standard pour Java et compiler le code en code machine natif et l'exécuter dans le runtime qui gère les appels de nouveaux objets dans mallocs et l'accès aux fichiers dans les appels système sur la machine. Et c'est ce que fait le compilateur Ahead Of Time (AOT plutôt que JIT). Appelez cette exécution ce que vous ... vous pourriez l' appeler une implémentation machine virtuelle Java (et il ne suit la spécification JVM) ou un environnement d'exécution ou lib standard pour Java. C'est là et ça fait essentiellement la même chose.

Cela pourrait être fait soit en réimplémentant javacpour cibler la machine native (c'est un peu ce que GCJ a fait). Ou cela pourrait être fait en traduisant le code d'octet généré par javacen code machine (ou octet) pour une autre machine - c'est ce que fait Android. Basé sur Wikipedia, c'est aussi ce que fait Excelsior JET ("Le compilateur transforme le code d'octet Java portable en exécutables optimisés pour le matériel et le système d'exploitation (OS) souhaités"), et il en va de même pour RoboVM .

Il y a des complications supplémentaires avec Java, ce qui signifie qu'il est très difficile de le faire en tant qu'approche exclusive. Le chargement dynamique des classes ( class.forName()) ou des objets mandatés nécessite une dynamique que les compilateurs AOT ne fournissent pas facilement et leurs JVM respectives doivent donc également inclure un compilateur JIT (Excelsior JET) ou un interprète (GCJ) pour gérer les classes qui ne peuvent pas être précompilées dans originaire de.

N'oubliez pas que la JVM est une spécification , avec de nombreuses implémentations . La bibliothèque standard C est également une spécification avec de nombreuses implémentations différentes.

Avec Java8, un bon travail a été fait sur la compilation AOT. Au mieux, on ne peut résumer AOT en général que dans les limites de textbox. Cependant, lors du JVM Language Summit pour 2015 (août 2015), il y avait une présentation: Java Goes AOT (vidéo youtube). Cette vidéo dure 40 minutes et aborde de nombreux aspects techniques plus approfondis et des performances de référence.


Désolé, je ne sais pas grand-chose à ce sujet, mais cela signifie-t-il que Java est natif maintenant? Ou cela signifie-t-il qu'il existe un nouvel indicateur de compilateur qui nous permet de compiler des programmes java en code natif si nous le voulons, et nous avons toujours la possibilité de compiler en code octet également?
Pavel

@ paulpaul1076 Je suggère de regarder la vidéo que j'ai liée. Il contient un peu plus que je ne peux raisonnablement intégrer dans un commentaire.

4

gcj exemple exécutable minimal

Vous pouvez également observer une implémentation open source comme gcj(maintenant obsolète). Exemple de fichier Java:

public class Main {
    public static void main(String args[]) {
        System.out.println("hello world");
    }
}

Ensuite, compilez et exécutez avec:

gcj -c Main.java
gcj --main=Main -o Main Main.o
./Main

Vous êtes maintenant libre de le décompiler et de voir comment cela fonctionne.

file Main.o dit que c'est un fichier elfe.

readelf -d Main | grep NEEDED dit que cela dépend des bibliothèques dynamiques:

0x0000000000000001 (NEEDED)             Shared library: [libgcj.so.14]
0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

Donc libgcj.so doit être l'endroit où la fonctionnalité Java est implémentée.

Vous pouvez ensuite le décompiler avec:

objdump -Cdr Main.o

et voyez exactement comment il est mis en œuvre.

Ressemble beaucoup au C ++, à beaucoup d'appels de noms et de fonctions polymorphes indirectes.

Je me demande comment la collecte des ordures intervient. Cela vaut la peine d'être examiné: /programming/7100776/garbage-collection-implementation-in-compiled-languages et d'autres langages compilés avec GC comme Go.

Testé sur Ubuntu 14.04, GCC 4.8.4.

Jetez également un œil à https://en.wikipedia.org/wiki/Android_Runtime , l'épine dorsale d'Android 5, qui fait le plein AOT pour optimiser les applications Android.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.