it-swarm.com.de

Wie funktionieren Java AOT-Compiler?

Es gibt einige Tools ( Excelsior JET usw.), die behaupten, Java App's in native ausführbare Dateien (*.exe) Zu transformieren. Nach meinem Verständnis erstellen diese Tools jedoch nur native Wrapper, die Java über eine Shell oder eine Befehlszeile aufrufen/ausführen.

Wenn dieses Verständnis falsch ist, sehe ich nicht, wie es sein könnte. Wenn eine laufende JVM (Java Prozess) im Wesentlichen ein Hochleistungsinterpreter ist, der Bytecode aus Java Klassendateien im laufenden Betrieb lädt, dann sehe ich nicht, wie ein Java App (eine Sammlung von Bytecode-Dateien, die als Eingabe für eine JVM dienen) könnte jemals wirklich in eine ausführbare Datei konvertiert werden.

Dies liegt daran, dass der JVM-Prozess bereits eine native ausführbare Datei ist, die Sätze von Bytecodedateien als Eingabe verwendet. Das Zusammenführen dieser Bytecode-Dateien und des JVM-Prozesses zu einer einzigen, einheitlichen nativen ausführbaren Datei scheint nicht möglich zu sein, ohne die JVM vollständig neu zu schreiben und das Railing aus der JVM-Spezifikation zu entfernen.

Also frage ich: Wie "transformieren" diese Tools tatsächlich Java Klassendateien in eine native ausführbare Datei oder tun sie)? ?

19
smeeb

Alle Programme haben eine Laufzeitumgebung. Wir neigen dazu, dies zu vergessen, aber es ist da. Standardbibliothek für C, die Systemaufrufe an das Betriebssystem umschließt. Die Laufzeit von Objective-C umfasst die gesamte Nachrichtenübermittlung.

Bei Java ist die Laufzeit die JVM. Die meisten Java-Implementierungen, mit denen die Benutzer vertraut sind, ähneln der HotSpot-JVM, einem Bytecode-Interpreter und JIT-Compiler.

Dies muss nicht die einzige Implementierung sein. Es gibt absolut nichts zu sagen, dass Sie keine standardmäßige lib-ähnliche Laufzeit für Java) erstellen und den Code in nativen Maschinencode kompilieren und innerhalb der Laufzeit ausführen können, die Aufrufe für neue Objekte in mallocs und verarbeitet Dateizugriff auf Systemaufrufe auf dem Computer. Und genau das macht der Ahead Of Time-Compiler (AOT statt JIT). Nennen Sie diese Laufzeit so, wie Sie wollen ... Sie könnten es eine JVM-Implementierung nennen (und es macht folgen Sie der JVM-Spezifikation) oder einer Laufzeitumgebung oder Standardbibliothek für Java. Es ist da und es macht im Wesentlichen das Gleiche.

Dies kann entweder durch erneutes Implementieren von javac erfolgen, um auf die native Maschine abzuzielen (genau das hat GCJ getan). Oder es könnte mit der Übersetzung des von javac generierten Bytecodes in Maschinencode (oder Bytecode) für eine andere Maschine geschehen - das ist, was Android tut. Basierend auf Wikipedia) das macht auch Excelsior JET ("Der Compiler wandelt den portablen Java Bytecode in optimierte ausführbare Dateien für die gewünschte Hardware und das gewünschte Betriebssystem) um"), und das gilt auch für RoboVM .

Es gibt zusätzliche Komplikationen mit Java, das heißt, dies ist sehr als exklusiver Ansatz schwer zu tun. Das dynamische Laden von Klassen (class.forName()) oder Proxy-Objekte erfordert eine Dynamik, die AOT-Compiler nicht einfach bereitstellen können. Daher müssen ihre jeweiligen JVMs entweder einen JIT-Compiler (Excelsior JET) oder einen enthalten Interpreter (GCJ) für Klassen, die nicht in native vorkompiliert werden konnten.

Denken Sie daran, die JVM ist eine Spezifikation , mit vielen Implementierungen . Die C-Standardbibliothek ist auch eine Spezifikation mit vielen verschiedenen Implementierungen.

Mit Java8 wurde einiges an Arbeit an der AOT-Kompilierung geleistet. Bestenfalls kann man AOT im Allgemeinen nur innerhalb der Grenzen des Textfelds zusammenfassen. Auf dem JVM Language Summit 2015 (August 2015) gab es jedoch eine Präsentation: Java Goes AOT (Youtube-Video). Dieses Video ist 40 Minuten lang und geht auf viele der tieferen technischen Aspekte und Leistungsbenchmarks ein.

27
user40980

gcj minimales ausführbares Beispiel

Sie können auch eine Open Source-Implementierung wie gcj (jetzt veraltet) beobachten. Z.B. Java Datei:

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

Dann kompilieren und ausführen mit:

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

Jetzt können Sie es dekompilieren und sehen, wie es funktioniert.

file Main.o Sagt, dass es sich um eine Elfendatei handelt.

readelf -d Main | grep NEEDED Sagt, dass es von den dynamischen Bibliotheken abhängt:

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

libgcj.so muss also dort sein, wo die Java - Funktionalität implementiert ist.

Sie können es dann dekompilieren mit:

objdump -Cdr Main.o

und sehen Sie genau, wie es implementiert ist.

Sieht ähnlich aus wie C++, viele Namensmangel und indirekte polymorphe Funktionsaufrufe.

Ich frage mich, wie die Garbage Collection funktioniert. Es lohnt sich, Folgendes zu untersuchen: https://stackoverflow.com/questions/7100776/garbage-collection-implementation-in-compiled-languages und andere kompilierte Sprachen mit GC wie Go.

Getestet unter Ubuntu 14.04, GCC 4.8.4.

Schauen Sie sich auch https://en.wikipedia.org/wiki/Android_Runtime an, das Rückgrat von Android ab 5, das AOT zur Optimierung von Android Apps voll ausführt.