Як працюють компілятори 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.

Це не повинно бути єдиною реалізацією. Абсолютно нічого не говорить про те, що ви не можете створити стандартний час виконання Java для Java та скомпілювати код у кодовому машинному коді та запустити його під час виконання, який обробляє виклики нових об’єктів у mallocs та отримує доступ до системних викликів на машині. І ось що робить компілятор Ahead Of Time (AOT, а не JIT). Назвіть цей час виконання тим, що ви хочете ... ви можете назвати це реалізацією JVM (і це робить слід специфікаціям JVM) або середовищі виконання або стандартну Бібліотеці для Java. Його є, і це робить по суті те ж саме.

Це можна зробити або шляхом повторного впорядкування, javacщоб орієнтуватися на рідну машину (це те, що робив GCJ ). Або це можна зробити з перекладом байтового коду, згенерованого javacв машинний (або байтний) код для іншої машини - ось що робить Android. На основі Вікіпедії це робить і Excelsior JET ("Компілятор перетворює переносний байт-код Java в оптимізовані виконувані файли для потрібної апаратної та операційної системи (ОС)"), і те саме стосується RoboVM .

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

Пам'ятайте, що JVM є специфікацією , з багатьма реалізаціями . Стандартна бібліотека С - це також специфікація з багатьма різними реалізаціями.

З Java8 було зроблено досить багато роботи над компіляцією AOT. У кращому випадку можна лише узагальнити AOT загалом у межах текстового поля. Однак на Мовному саміті JVM за 2015 рік (серпень 2015 року) відбулася презентація: Java Goes AOT (відео YouTube). Це відео триває 40 хвилин і містить багато глибших технічних аспектів та показників продуктивності.


Вибачте, я мало що про це знаю, але чи означає це, що ява зараз уроджен? Або це означає, що є новий прапор компілятора, який дозволяє нам компілювати програми 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.