Wie funktionieren Java AOT-Compiler?

18

Es gibt eine Reihe von Tools ( Excelsior JET usw.), die behaupten, Java-Apps in native ausführbare Dateien ( *.exe) zu verwandeln . Ich verstehe jedoch, dass diese Tools eigentlich nur native Wrapper erstellen, die javaüber eine Shell oder eine Befehlszeile aufgerufen / ausgeführt werden.

Wenn dieses Verständnis falsch ist, sehe ich nicht, wie es sein könnte. Wenn eine laufende JVM (ein javaProzess) im Grunde genommen ein Hochleistungsinterpreter ist, der Bytecode direkt aus Java-Klassendateien lädt, kann ich mir nicht vorstellen, wie eine Java-App (eine Sammlung von Bytecode-Dateien, die als Eingabe für eine JVM dienen) jemals aussehen könnte wirklich in eine ausführbare Datei umgewandelt.

Dies liegt daran, dass der JVM-Prozess bereits eine native ausführbare Datei ist, die Sätze von Bytecode-Dateien als Eingabe verwendet. Das Zusammenführen dieser Bytecodedateien 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 die Reling aus der JVM-Spezifikation zu entfernen.

Also frage ich: Wie diese Werkzeuge tatsächlich „verwandeln“ Java - Klassendateien in eine native ausführbare Datei, oder tun sie?

smeeb
quelle

Antworten:

26

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

Bei Java ist die Laufzeit die JVM. Die meisten bekannten Java-Implementierungen ähneln der HotSpot-JVM, bei der es sich um einen Bytecode-Interpreter und einen JIT-Compiler handelt.

Dies muss nicht die einzige Implementierung sein. Es gibt absolut nichts zu sagen, dass Sie keine standardmäßige lib-esque-Laufzeit für Java erstellen und den Code in systemeigenen Maschinencode kompilieren und diesen innerhalb der Laufzeit ausführen können, die Aufrufe für neue Objekte in mallocs und Dateizugriffe in Systemaufrufe auf der Maschine verarbeitet. Und genau das macht der AOT-Compiler (AOT anstatt JIT). Rufen Sie diese Laufzeit , was Sie ... Sie es eine JVM Implementierung nennen könnte (und es ist die JVM - Spezifikation folgen) oder eine Laufzeitumgebung oder Standard - lib für Java. Es ist da und es macht im Wesentlichen das Gleiche.

Dies könnte entweder durch eine Neuimplementierung erfolgen, javacum auf den nativen Computer abzuzielen (so wie es GCJ getan hat). Oder Sie können den von generierten Byte-Code javacin Maschinencode (oder Byte-Code) für eine andere Maschine umwandeln - genau das macht Android. Basierend auf Wikipedia ist es auch das, was Excelsior JET macht ("Der Compiler wandelt den portablen Java-Byte-Code in optimierte ausführbare Dateien für die gewünschte Hardware und das gewünschte Betriebssystem (OS) um"), und dasselbe gilt für RoboVM .

Es gibt zusätzliche Komplikationen mit Java, was bedeutet, dass dies als exklusiver Ansatz sehr schwierig ist. Das dynamische Laden von Klassen ( class.forName()) oder Proxy-Objekten erfordert eine Dynamik, die von AOT-Compilern nicht einfach bereitgestellt werden kann. Daher müssen die entsprechenden JVMs entweder einen JIT-Compiler (Excelsior JET) oder einen Interpreter (GCJ) enthalten, um Klassen verarbeiten zu können, in die keine Vorkompilierung möglich ist gebürtig.

Denken Sie daran, das ist JVM 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 generell nur innerhalb der Grenzen der Textbox zusammenfassen. Beim 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.


quelle
Entschuldigung, ich weiß nicht viel darüber, aber heißt das, dass Java jetzt einheimisch ist? Oder bedeutet dies, dass es ein neues Compiler-Flag gibt, mit dem wir Java-Programme nach Belieben in systemeigenen Code kompilieren können, und dass wir weiterhin die Möglichkeit haben, auch Byte-Code zu kompilieren?
Pavel
@ paulpaul1076 Ich würde vorschlagen, das von mir verlinkte Video anzuschauen. Es ist ziemlich viel mehr drin, als ich vernünftigerweise in einen Kommentar einfügen kann.
4

gcj minimal ausführbares Beispiel

Sie können auch eine Open-Source-Implementierung wie gcj(jetzt veraltet) beobachten. ZB 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, es sei eine Elfendatei.

readelf -d Main | grep NEEDED Das hängt von den dynamischen Bibliotheken ab:

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

Daher muss in libgcj.so die Java-Funktionalität implementiert sein.

Sie können es dann dekompilieren mit:

objdump -Cdr Main.o

und sehen genau, wie es umgesetzt wird.

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

Ich frage mich, wie sich die Garbage Collection auswirkt. Es lohnt sich, einen Blick darauf zu werfen : /programming/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 Backbone von Android 5 und höher, das AOT-Funktionen zur Optimierung von Android-Apps bietet.

Ciro Santilli ist ein Schauspieler
quelle