Come funzionano i compilatori Java AOT?


18

Esistono pochi strumenti ( Excelsior JET , ecc.) Che pretendono di trasformare le app Java in eseguibili nativi ( *.exe). Tuttavia, ho capito che questi strumenti stanno davvero creando involucri nativi che invocano / eseguono javada una shell o da una riga di comando.

Se questa comprensione non è corretta, non vedo come potrebbe essere. Se una JVM ( javaprocesso) in esecuzione è essenzialmente un interprete ad alte prestazioni, caricando al volo il bytecode dai file di classe Java, non vedo come un'app Java (una raccolta di file bytecode che fungono da input per una JVM) potrebbe mai essere veramente convertito in un eseguibile.

Questo perché il processo JVM è già un eseguibile nativo che accetta come input insiemi di file bytecode. Unire quei file bytecode e il processo JVM in un unico eseguibile nativo unificato non sembra possibile senza riscrivere completamente la JVM e de-railing dalle specifiche JVM.

Quindi chiedo: in che modo questi strumenti "trasformano" effettivamente i file di classe Java in un eseguibile nativo o lo fanno?

Risposte:


26

Tutti i programmi hanno un ambiente di runtime. Tendiamo a dimenticarlo, ma è lì. Lib standard per C che avvolge le chiamate di sistema al sistema operativo. Objective-C ha il suo runtime che avvolge tutto il suo passaggio di messaggi.

Con Java, il runtime è JVM. La maggior parte delle implementazioni Java che le persone conoscono sono simili a HotSpot JVM che è un interprete di codice byte e un compilatore JIT.

Questa non deve essere l'unica implementazione. Non c'è assolutamente nulla che dica che non è possibile creare un runtime lib-esque standard per Java e compilare il codice in codice macchina nativo ed eseguirlo all'interno del runtime che gestisce le chiamate per nuovi oggetti in malloc e l'accesso ai file nelle chiamate di sistema sul computer. Ed è quello che fa il compilatore Ahead Of Time (AOT piuttosto che JIT). Chiamata che runtime ciò che si ... si potrebbe chiamare un'implementazione JVM (e non seguire la specifica JVM) o di un ambiente di runtime o lib standard per Java. È lì e fa essenzialmente la stessa cosa.

Potrebbe essere fatto sia reimplementando javacper colpire la macchina nativa (che è un po 'come ha fatto GCJ ). Oppure si potrebbe fare con la traduzione del codice byte generato da javacin codice macchina (o byte) per un altro computer - ecco cosa fa Android. Basato su Wikipedia è quello che fa anche Excelsior JET ("Il compilatore trasforma il codice byte Java portatile in eseguibili ottimizzati per l'hardware e il sistema operativo desiderati"), e lo stesso vale per RoboVM .

Ci sono ulteriori complicazioni con Java, il che significa che questo è molto difficile da fare come approccio esclusivo. Il caricamento dinamico di classi ( class.forName()) o oggetti proxy richiede una dinamica che i compilatori AOT non forniscono facilmente e quindi le rispettive JVM devono includere anche un compilatore JIT (Excelsior JET) o un interprete (GCJ) per gestire le classi in cui non è possibile precompilare nativo.

Ricorda, JVM è una specifica , con molte implementazioni . La libreria standard C è anche una specifica con molte implementazioni diverse.

Con Java8, un bel po 'di lavoro è stato fatto sulla compilazione di AOT. Nella migliore delle ipotesi, si può semplicemente riassumere AOT in generale entro i confini della casella di testo. Tuttavia, nel Summit linguistico JVM per il 2015 (agosto 2015), è stata presentata una presentazione: Java Goes AOT (video di YouTube). Questo video dura 40 minuti e approfondisce molti degli aspetti tecnici più profondi e dei benchmark delle prestazioni.


Spiacente, non ne so molto, ma questo significa che Java è nativo ora? Oppure significa che c'è un nuovo flag di compilatore che ci consente di compilare programmi Java in codice nativo se vogliamo, e abbiamo ancora la possibilità di compilare anche in codice byte?
Pavel,

@ paulpaul1076 Suggerirei di guardare il video che ho collegato. C'è un po 'più di quello che posso ragionevolmente inserire in un commento.

4

gcj esempio eseguibile minimo

Puoi anche osservare un'implementazione open source come gcj(ora obsoleta). Ad esempio file Java:

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

Quindi compilare ed eseguire con:

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

Ora sei libero di decompilarlo e vedere come funziona.

file Main.o dice che è un file elfo.

readelf -d Main | grep NEEDED dice che dipende dalle librerie dinamiche:

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

Quindi libgcj.so deve essere dove è implementata la funzionalità Java.

È quindi possibile decompilarlo con:

objdump -Cdr Main.o

e vedere esattamente come è implementato.

Assomiglia molto al C ++, a molte manomissioni dei nomi e alle chiamate di funzioni polimorfiche indirette.

Mi chiedo come entri nella garbage collection. Vale la pena esaminare: /programming/7100776/garbage-collection-implementation-in-compiled-languages e altri linguaggi compilati con GC come Go.

Testato su Ubuntu 14.04, GCC 4.8.4.

Dai anche un'occhiata a https://en.wikipedia.org/wiki/Android_Runtime , la spina dorsale di Android 5 in poi, che fa pieno AOT per ottimizzare le app Android.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.