JEP 514: Ahead-of-Time Command-Line Ergonomics
Ahead-of-Time (заблаговременная компиляция и подготовка): эргономика командной строки
| Ответственный | John Rose |
| Тип | Feature |
| Область | JDK |
| Статус | Closed / Delivered |
| Выпуск | 25 |
| Компонент | hotspot / runtime |
| Обсуждение | leyden dash dev at openjdk dot org |
| Трудоёмкость | M |
| Длительность | S |
| Связан с | JEP 483: Ahead-of-Time Class Loading & Linking |
| Рецензенты | Alex Buckley, Vladimir Kozlov |
| Одобрен | Vladimir Kozlov |
| Создан | 2025/02/13 18:07 |
| Обновлён | 2026/08/25 18:23 |
| Задача | 8350022 |
Аннотация
Упростить создание AOT Cache (кэш, подготовленный заранее), ускоряющих запуск Java-приложений, за счёт упрощения команд, необходимых в типичных сценариях использования.
Цели
-
Упростить процесс создания AOT-кэша без потери выразительности.
-
Не вводить принципиально новые AOT-сценарии работы, а упростить доступ к существующим.
Что не является целью
- Вводить новые AOT-оптимизации не является целью. Мы стремимся упростить доступ ко всем AOT-оптимизациям, как существующим, так и будущим.
Мотивация
AOT-кэши, появившиеся в JEP 483, ускоряют запуск Java-приложений. Ожидается, что их польза будет расти по мере того, как проект Leyden будет добавлять в HotSpot JVM новые оптимизации, связанные с AOT.
В JDK 24 AOT-кэш создаётся в два шага: лаунчер java вызывается в двух разных режимах AOT. При первом вызове указывается режим record, и JVM наблюдает за динамикой обучающего запуска вашего приложения и записывает её в AOT-конфигурацию. При втором вызове указывается режим create, и JVM создаёт AOT-кэш на основе конфигурации, записанной во время обучающего запуска.
Вот пример такого двухшагового сценария, взятый из JEP 483:
$ java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf \
-cp app.jar com.example.App ...
$ java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf \
-XX:AOTCache=app.aot
После этого вы запускаете приложение, указывая только AOT-кэш:
$ java -XX:AOTCache=app.aot -cp app.jar com.example.App ...
Такой рабочий запуск приложения выполняется быстрее, потому что классы приложения не нужно обнаруживать, загружать и связывать. Они сразу доступны из кэша.
Запускать java дважды, чтобы создать AOT-кэш, неудобно. Неудобно и то, что после этого остаётся файл AOT-конфигурации — это всего лишь временный файл, который не нужен для рабочих запусков и может быть удалён.
Было бы гораздо удобнее, если бы, по крайней мере в типичных сценариях, для обучающего запуска и создания AOT-кэша был нужен только один шаг. Это было бы эффективно для пользователей и удобно для таких приложений, как JRuby, которые сами организуют обучение на собственных нагрузках. Возможность явно указывать режимы AOT и AOT-конфигурации может остаться для нетипичных сценариев.
Описание
Мы добавляем в лаунчер java новый параметр командной строки AOTCacheOutput, задающий выходной файл AOT-кэша. Если этот параметр используется отдельно, без других параметров AOT, лаунчер фактически разделяет свой вызов на два подвызова: первый выполняет обучающий запуск (AOTMode=record), а затем второй создаёт AOT-кэш (AOTMode=create).
Например, показанный ранее двухшаговый сценарий можно заменить одним шагом:
$ java -XX:AOTCacheOutput=app.aot -cp app.jar com.example.App ...
Для удобства при такой работе JVM создаёт временный файл для AOT-конфигурации и удаляет его по завершении.
Рабочий запуск с использованием AOT-кэша выполняется так же, как и раньше:
$ java -XX:AOTCache=app.aot -cp app.jar com.example.App ...
С помощью новой переменной окружения JDK_AOT_VM_OPTIONS можно передавать параметры командной строки, которые применяются только к подвызову, создающему кэш, и не влияют на подвызов, выполняющий обучающий запуск. Синтаксис тот же, что и у существующей переменной окружения JAVA_TOOL_OPTIONS. Благодаря этому одношаговый сценарий можно применять даже там, где из-за различий в параметрах командной строки может показаться, что необходим двухшаговый сценарий.
Полная спецификация этих параметров доступна здесь.
Ручное управление обучением и созданием кэша
Остаются сценарии, в которых может быть предпочтительнее двухшаговый сценарий с явным указанием режима AOT при каждом вызове (AOTMode=create, затем AOTMode=record).
Например, если вы собираетесь развернуть приложение на небольших экземплярах в облаке, то можете выполнить обучающий запуск на небольшом экземпляре, а AOT-кэш создать на большом. Так обучающий запуск отражает среду развёртывания, а при создании AOT-кэша можно задействовать дополнительные процессорные ядра и память большого экземпляра.
Такое разделение труда может стать важнее по мере усложнения AOT-оптимизаций Leyden. Например, какой-нибудь будущей AOT-оптимизации может потребоваться несколько минут на создание кэша на небольшом экземпляре, но всего несколько секунд на большом.
Кроме того, в средах с ограниченными ресурсами одношаговый сценарий может работать не так, как ожидается. Подвызов, создающий AOT-кэш, использует собственную Java-кучу того же размера, что и куча обучающего запуска. В результате для выполнения одношагового сценария требуется вдвое больше памяти, чем размер кучи, указанный в командной строке. Например, если одношаговый сценарий java -XX:AOTCacheOutput=... сопровождается параметром -Xms4g -Xmx4g, задающим кучу размером 4 ГБ, то для выполнения сценария среде нужно 8 ГБ. Пользователям в средах с ограниченными ресурсами следует использовать двухшаговый сценарий, если одношаговый не завершается успешно.
Альтернативы
- Мы рассматривали комбинированный режим AOT
AOTMode=record+create. Сейчас это могло бы работать, но в будущем мы можем расширить обучающие запуски так, чтобы они читали существующий AOT-кэш. Тогда параметр-XX:AOTCache=myapp.aotстал бы неоднозначным, и нам, скорее всего, всё равно пришлось бы ввести параметрAOTCacheOutput.