Метрополис был создан для изучения технологий Java-on-Java, и действительно, результаты которого пришли вместе с Java-9 с введением в Интерфейс компилятора JVM уровня Java (JVMCI), предназначенный для компиляции AOT для сокращения времени запуска как малых, так и больших приложений Java путем компиляции классов в собственный код перед запуском виртуальной машины.

Цитируя сам JEP, JVMCI API состоит из механизмов для:

- Доступ к структурам данных виртуальной машины, требуемым оптимизирующим компилятором преобразования байт-кода в машинный код, таким как классы, поля, методы, информация о профилировании и т. Д.

- Установка скомпилированного кода вместе со всеми метаданными, необходимыми JVM для управления скомпилированным кодом, таким как карты GC и информация для поддержки деоптимизации.

- Подключение к системе компиляции JVM для обработки запросов JVM для создания машинного кода для методов.

Это, безусловно, упростит обслуживание и, как мы надеемся, улучшит компилятор JVM по сравнению с существующими решениями.

Один непосредственный поставщик услуги развился с введением Грааль с выпуском Java10 для GA. , где он доступен в JDK для систем, отличных от 64-битных систем Linux (для которых он был ограничен до JDK / 9).

Предварительно любой, кто хочет использовать JVMCI или убедиться, что он используется JVM, и выбор пользовательского компилятора может спросить:

Как мне проверить и убедиться, что JVMCI включен и используется ли он JVM (компилятором)?

Как объяснил Дуг в потоке OpenJDK, можно использовать следующий блок кода, чтобы проверить это в своей системе:

private static void JVMCIQueries() {
    // Check if the JDK used supports JVMCI?
    String vm_version = System.getProperty("java.vm.version");
    System.out.printf("java.vm.version = %s%n", vm_version);
    com.sun.management.HotSpotDiagnosticMXBean bean = java.lang.management.ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
    // Is JVMCI enabled by default?
    com.sun.management.VMOption enableJVMCI = bean.getVMOption("EnableJVMCI");
    System.out.println(enableJVMCI);
    // Is the system using the JVMCI compiler for normal compilations?
    com.sun.management.VMOption useJVMCICompiler = bean.getVMOption("UseJVMCICompiler");
    System.out.println(useJVMCICompiler);
    // What compiler is selected?
    String compiler = System.getProperty("jvmci.Compiler");
    System.out.printf("jvmci.Compiler = %s%n", compiler);
}

При выполнении вышеуказанного в Mac OS (x64) с JDK 10 в результате получится следующее

После этого вы можете включить JVMCI (еще экспериментальный), используя флаг -

-XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI

В отдельном прогоне он преобразует существующий вывод в следующий

Это означает, что, хотя JVMCI сейчас включен, он все еще не используется. Чтобы использовать JVMCI, нужно было бы добавить флаг -

-XX:+UseJVMCICompiler

что в конечном итоге означало бы, что система будет использовать компилятор на основе JVMCI для обычных компиляций.

А что насчет значения jvmci.Compiler, наконец? Необходимо сделать еще один шаг, чтобы фактически использовать компилятор, основанный на интерфейсе (единственный пример в настоящее время - грааль). Например, это можно сделать с помощью флага -

-Djvmci.Compiler=graal

…вот и все! Теперь можно скомпилировать с помощью компилятора Graal, используя указанные выше флаги.

GraalVM - одно из недалеких приложений, использующих преимущества компиляции и профилирования Graal, которые могли бы стать возможной заменой существующей VisualVM, поддерживаемой до JDK-8.

Одна из других реализаций Graal находится в самом JDK. Модуль, представляющий это изменение в JDK, - это модуль `jdk.aot`.

Предварительная компиляция (AOT) использует Graal в качестве серверной части для генерации кода. Фактически он заранее компилирует байт-код в собственный код (файл общих объектов в формате ELF) и может применяться к любому байт-коду.

Но правда ли, что AOT - это экспериментальная функция?

AOT не является полноценным с точки зрения совместимости с текущими API Java. Некоторые из его ограничений также перечислены в предложении:

  • Пользователи должны использовать один и тот же JDK для компиляции и выполнения. Информация о версии `jaotc` для использования компиляции AOT.
  • jaotc, используемый для компиляции, добавляется как часть библиотек и проверяется во время загрузки. Если среда выполнения Java обновлена, вам необходимо перекомпилировать скомпилированные модули AOT перед их выполнением. Несовпадение версий JDK, используемых для компиляции и выполнения, может привести к сбою приложения.
  • Лямбда-выражения и другие сложные концепции Java, использующие динамически генерируемые классы во время выполнения, в настоящее время не поддерживаются компилятором AOT.
  • Для создания файлов с общими объектами (.so) в системе должна быть предварительно установлена ​​программа libelf.
  • Режим логической компиляции для java.base является многоуровневым AOT, поскольку для достижения максимальной производительности желательна JIT-перекомпиляция методов java.base. Неуровневая компиляция AOT имеет смысл только в определенных сценариях. Это включает в себя приложения, требующие предсказуемого поведения, когда занимаемая площадь более важна, чем пиковая производительность, или для систем, в которых генерация динамического кода не разрешена. В этих случаях компиляция AOT должна выполняться для всего приложения и, таким образом, является экспериментальной в JDK.

Эти ограничения могут быть устранены в будущих выпусках, и именно тогда я уверен, что тег Experimental из функции будет удален.

Если AOT является экспериментальным, то как мы можем скомпилировать модуль с использованием AOT?

Чтобы использовать компиляцию AOT, код приложения должен быть скомпилирован с помощью инструмента jaotc, находящегося в папке JDK_HOME / bin, учитывая несколько ограничений, перечисленных выше.

Как указано в Предварительная компиляция: использование AOT, если библиотека AOT была скомпилирована с использованием инструмента как:

jaotc — output libHelloWorld.so HelloWorld.class

его можно использовать на этапе выполнения как:

java -XX:AOTLibrary=./libHelloWorld.so HelloWorld

при условии, что конфигурации JVM одного и того же выпуска используются как во время компиляции, так и во время выполнения.

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

-XX:+/-UseAOT

Чтобы распечатать соответствующую информацию о компиляции, можно также переключить флаг -

-XX:+/-PrintAOT

Что более важно и относится к нашему варианту использования выше и даже как четко указано в разделе Риски и предположения предложения:

Если пользователь обнаруживает, что приложение запускается медленнее, не достигает ожидаемой пиковой производительности или дает сбой, он может просто выключить AOT с помощью флага `-XX: -UseAOT` или удалить любые библиотеки AOT.

В те ранние годы учебы я никогда не думал, что написание и использование компилятора может показаться таким простым. Открытый интерфейс, безусловно, получит еще больший вклад сообщества, когда он станет стабильным и совместимым с API Java (возможно, с JDK / 11?).