Как работают компиляторы Java AOT?


18

Существует несколько инструментов ( Excelsior JET и т. Д.), Которые претендуют на преобразование приложений Java в собственные исполняемые файлы ( *.exe). Однако, насколько я понимаю , эти инструменты на самом деле просто создают нативные обертки, которые запускаются / выполняются javaиз оболочки или командной строки.

Если это понимание неверно, я не понимаю, как это могло быть. Если работающий JVM ( javaпроцесс) по сути является высокопроизводительным интерпретатором, загружающим байт-код из файлов классов Java на лету, то я не вижу, каким образом приложение Java (коллекция файлов байт-кода, служащих в качестве входных данных для JVM) могло бы быть действительно превращается в исполняемый файл.

Это потому, что процесс JVM уже является собственным исполняемым файлом, который принимает наборы файлов байт-кода в качестве входных данных. Объединить эти файлы байт-кода и процесс JVM в единый унифицированный собственный исполняемый файл не представляется возможным без полного переписывания JVM и исключения из спецификации JVM.

Поэтому я спрашиваю: как эти инструменты на самом деле «превращают» файлы классов Java в собственный исполняемый файл или нет?

Ответы:


26

Все программы имеют среду выполнения. Мы склонны забывать это, но его там. Стандартная библиотека для C, которая упаковывает системные вызовы в операционную систему. Objective-C имеет среду выполнения, которая оборачивает всю передачу сообщений.

В Java среда выполнения - это JVM. Большинство реализаций Java, с которыми знакомы люди, похожи на JVM HotSpot, который является интерпретатором байтового кода и JIT-компилятором.

Это не должно быть единственной реализацией. Ничто не говорит о том, что вы не можете построить стандартную среду выполнения lib-esque для Java, скомпилировать код в машинный код и запустить его в среде выполнения, которая обрабатывает вызовы новых объектов в malloc и доступ к файлам в системные вызовы на машине. И это то, что делает компилятор Ahead Of Time (AOT, а не JIT). Называйте эту среду выполнения как хотите ... вы можете назвать это реализацией JVM (и она действительно следует спецификации JVM) или средой выполнения или стандартной библиотекой Java для Java. Его там и он делает то же самое.

Это можно сделать, либо переопределив javacцелевую машину (это то, что сделал GCJ ). Или это можно сделать с помощью перевода байт-кода, сгенерированного с помощью, javacв машинный (или байтовый) код для другой машины - это то, что делает Android. Основанный на Википедии , это то же самое делает Excelsior JET («Компилятор преобразует переносимый байт-код Java в оптимизированные исполняемые файлы для требуемого оборудования и операционной системы (ОС)»), и то же самое относится и к RoboVM .

С Java есть дополнительные сложности, которые означают, что это очень трудно сделать как эксклюзивный подход. Динамическая загрузка классов ( class.forName()) или прокси-объектов требует динамики, которую компиляторы AOT не могут легко обеспечить, поэтому их соответствующие JVM должны также включать в себя JIT-компилятор (Excelsior JET) или интерпретатор (GCJ) для обработки классов, которые не могут быть предварительно скомпилированы в родной.

Помните, что JVM является спецификацией , со многими реализациями . Стандартная библиотека C также является спецификацией с множеством различных реализаций.

С Java8 была проделана большая работа по компиляции AOT. В лучшем случае можно обобщить АОТ в целом только в пределах текстового поля. Тем не менее, на Языковом саммите JVM в 2015 году (август 2015 года) была представлена ​​презентация: Java Goes AOT (видео на YouTube). Это видео длится 40 минут и раскрывает многие более глубокие технические аспекты и показатели производительности.


Извините, я мало что знаю об этом, но значит ли это, что Java теперь является родным? Или это означает, что есть новый флаг компилятора, который позволяет нам компилировать Java-программы в нативный код, если мы хотим, и у нас все еще есть возможность компилировать в байт-код?
Павел

@ paulpaul1076 Я бы предложил посмотреть видео, которое я связал. В нем гораздо больше, чем я могу обоснованно вписать в комментарий.

4

gcj минимальный работоспособный пример

Вы также можете наблюдать реализацию с открытым исходным кодом, как gcj(в настоящее время устарела). Например, файл Java:

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

Затем скомпилируйте и запустите:

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

Теперь вы можете декомпилировать его и посмотреть, как оно работает.

file Main.o говорит, что это файл эльфа.

readelf -d Main | grep NEEDED говорит, что это зависит от динамических библиотек:

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

Таким образом, libgcj.so должен быть там, где реализована функциональность Java.

Затем вы можете декомпилировать его:

objdump -Cdr Main.o

и посмотреть, как именно это реализовано.

Очень похоже на C ++, много искажений имен и косвенных вызовов полиморфных функций.

Интересно, как начинается сборка мусора. Стоит посмотреть: /programming/7100776/garbage-collection-implementation-in-compiled-languages и другие скомпилированные языки с GC, такие как Go.

Протестировано на Ubuntu 14.04, GCC 4.8.4.

Также взгляните на https://en.wikipedia.org/wiki/Android_Runtime , основу Android 5 и более поздних версий, которая полностью поддерживает AOT для оптимизации приложений Android.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.