Come funzionano i compilatori Java AOT?

16

Ci sono alcuni strumenti disponibili ( Excelsior JET , ecc.) che pretendono di trasformare le app Java in eseguibili nativi ( *.exe ). Tuttavia è mia comprensione che questi strumenti in realtà stanno solo creando wrapper nativi che invocano / eseguono java da una shell o da una riga di comando.

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

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

Quindi chiedo: in che modo questi strumenti in realtà "trasformano" i file di classe Java in un eseguibile nativo o no?

    
posta smeeb 05.06.2015 - 02:17
fonte

2 risposte

24

Tutti i programmi hanno un ambiente di runtime. Tendiamo a dimenticarlo, ma è lì. Lib standard per C che esegue il wrapping delle chiamate di sistema sul sistema operativo. Objective-C ha il suo runtime che avvolge tutto il suo messaggio che passa.

Con Java, il runtime è JVM. La maggior parte delle implementazioni Java a cui le persone hanno familiarità sono simili alla JVM HotSpot che è un interprete di codici byte e compilatore JIT.

Questa non deve essere l'unica implementazione. Non c'è assolutamente nulla che dica che non è possibile costruire un runtime lib-esque standard per Java e compilare il codice al codice macchina nativo ed eseguire quello all'interno del runtime che gestisce le chiamate per nuovi oggetti in malloc e l'accesso ai file nelle chiamate di sistema sulla macchina. E questo è quello che fa il compilatore Ahead Of Time (AOT piuttosto che JIT). Chiama quel runtime come vuoi ... potresti chiamarlo un'implementazione JVM (e fa seguire la specifica JVM) o un ambiente runtime o lib standard per Java. È lì e fa essenzialmente la stessa cosa.

Potrebbe essere eseguito reimplementando javac per indirizzare la macchina nativa (questo è un po 'come ha fatto GCJ ). Oppure si potrebbe fare con la traduzione del codice byte generato da javac in codice macchina (o byte) per un'altra macchina - questo è ciò che fa Android. Basato su Wikipedia è ciò che fa anche Excelsior JET ("Il compilatore trasforma il codice byte Java portatile in eseguibili ottimizzati per l'hardware desiderato e sistema operativo (OS) "), e lo stesso vale per RoboVM .

Ci sono ulteriori complicazioni con Java che significa che è molto difficile da fare come approccio esclusivo. Caricamento dinamico di classi ( class.forName() ) o oggetti con proxy richiedono dinamiche che i compilatori AOT non forniscono facilmente e quindi le rispettive JVM devono anche includere un compilatore JIT (Excelsior JET) o un interprete (GCJ) per gestire classi che non possono essere precompilate in native.

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

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

    
risposta data 05.06.2015 - 03:13
fonte
3

gcj esempio minimo eseguibile

Puoi anche osservare un'implementazione open source come gcj (ora obsoleta). Per 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 il luogo in cui è implementata la funzionalità Java.

Puoi quindi decompilarlo con:

objdump -Cdr Main.o

e vedere esattamente come è implementato.

Sembra molto simile al C ++, un sacco di manomissione dei nomi e chiamate di funzioni polimorfiche indirette.

Mi chiedo come entri la raccolta dei rifiuti. Vale la pena esaminare: link e altre lingue compilate con GC come Go.

Testato su Ubuntu 14.04, GCC 4.8.4.

Dai anche un'occhiata al link , la spina dorsale di Android 5 in poi, che completa AOT per ottimizzare le app Android.

    
risposta data 04.08.2015 - 13:09
fonte

Leggi altre domande sui tag