Через четыре месяца планируется выпустить Java 9 (надеемся, что переносов сроков больше не произойдёт). Ничто не мешает уже сейчас на предварительной версии проверить, насколько соответствует ожиданиям главная возможность новой версии — модульность (проект Jigsaw).



В статье описываются изменения, требуемые для сборки дистрибутива с поддержкой модульности. Сборка дистрибутива производится в операционных системах Windows, macOS и Linux.

Полный список новых функций JDK 9 впечатляет. Проект Jigsaw является главной целью версии 9. Предметом данной статьи является составная часть проекта JigsawJEP 275: Modular Java Application Packaging и связанные с ним:


В JDK входит утилита командной строки Java Packager, позволяющая выполнять компиляцию, сборку, создание цифровой подписи и дистрибутивов Java-приложений. Функции, которые выполняет Java Packager, начиная с JDK 7 Update 6, доступны и в виде задач (tasks) для Ant. В официальной документации они имеют наименование JavaFX Ant Tasks, т.к. ранее предназначались в первую очередь для JavaFX-приложений.

JEP 275: Modular Java Application Packaging направлен на интеграцию модульности в Java Packager, в том числе для создания образа JRE, включающего только те модули, которые использует приложение. JavaFX Ant Tasks так же могут быть вызваны и из Maven, доминирующего на сегодня инструмента сборки, посредством Maven AntRun Plugin.

В экспериментах использовались:


Код примера доступен на GitHub. Для компиляции и сборки дистрибутива используется Maven. Проект состоит из четырёх частей (модулей в терминологии Maven):

  • Консольное (console) приложение без использования модулей (модулей Java 9);
  • Консольное приложение с использованием модулей;
  • Настольное (desktop) приложение без использования модулей;
  • Настольное приложение с использованием модулей.

Сборка дистрибутивов выполняется во всех трёх операционных системах.

Сборка дистрибутива без использования модулей


Процесс сборки дистрибутива состоит из нескольких шагов:

  • Компиляция и создание файла JAR;
  • Формирование образа JRE;
  • Создание дистрибутива, включающих JAR, образ JRE и вспомогательные исполняемые файлы для запуска.

Для создания дистрибутивов в macOS и Linux ничего дополнительно не нужно (кроме JDK и Maven). В операционной системе Windows предварительно необходимо установить Inno Setup или WiX Toolset. Далее подразумевается, что используется Inno Setup.

Создание файла JAR при использовании Maven проще выполнить обычным способом, т.е. используя Apache Maven JAR Plugin (другой способ — использование <fx:jar>). В манифесте (файле MANIFEST.MF) файла JAR, используемом для сборки дистрибутива немодульного (без использования модулей Java 9) приложения, обязательно должен быть указан Main-Class. По этой причине использование вышеуказанного плагина должно быть явно задано с указанием в конфигурации основного класса.

Описание использования задачи <fx:deploy> в файле pom.xml:


Запуск компиляции и сборки дистрибутива в Windows и macOS:

mvn clean package -P native-deploy

Запуск компиляции и сборки дистрибутива в Linux (дополнительно создаётся файл архива tar.gz):

mvn clean package -P native-deploy,tar-gz

Файлы созданного дистрибутива с расширением exe, dmg располагаются в каталоге <module name>/target/deploy/native, с расширением tar.gz — в каталоге <module name>/target.

Добавление использования модулей


На этапе формирования образа JRE неявно будет задействован jlink, которому нужно передать информацию об используемых модулях.

Для определения, какие модули Java использует приложение, должна быть вручную запущена утилита jdeps. В версии 9 она дополнительно показывает используемые модули.

jdeps -s <name>.jar

Первое приложение (console) использует единственный модуль java.base, второе (desktop) — модули java.base и java.desktop (вместе с транзитивными модулями, т.к. java.desktop сам зависит от других модулей).

Для первого приложения требуется добавить файл module-info.java, содержащий (указание базового модуля java.base необязательно):

module console.modular {
}

Файл module-info.java для второго приложения должен содержать:

module desktop.modular {
    requires java.desktop;
}

В файле pom.xml появляются обязательные параметры пути к файлам модулей (путь к стандартным модулям $JAVA_HOME/jmods указывать необязательно) и наименования модуля приложения. Включать в качестве ресурса JAR приложения становится излишним, т.к. он уже включён в виде модуля в образ JRE.



Запуск компиляции и сборки аналогичен ранее указанному.

Сравнение результатов


Размер в мегабайтах образа JRE в составе дистрибутива и собственно файла дистрибутива, созданных с использованием Java Packager:
Приложение Windows macOS Linux
образ JRE дистрибутив (exe) образ JRE дистрибутив (dmg) образ JRE дистрибутив (tar.gz)
console-nonmodular 165 36 181 69 204 72
desktop-nonmodular 165 36 181 69 204 72
console-modular 35 9 35 13 44 16
desktop-modular 72 18 75 29 85 31

Размер в мегабайтах образа JRE, созданного при ручном запуске jlink с наиболее оптимальным набором параметров (недоступном из Java Packager):

jlink --module-path <module path> --add-modules <modules> --output <directory> --compress=2 --no-header-files --no-man-pages --strip-debug

Приложение Windows, образ JRE macOS, образ JRE Linux, образ JRE
console-modular 22 21 29
desktop-modular 38 39 49

Сильнее всего размер образа JRE уменьшился на macOS:

  • на 81% скриптом сборки при использовании Java Packager;
  • на 88% при использовании jlink.

Выводы


Значительное уменьшение размеров дистрибутива достигается относительно легко для простых проектов. В случае более сложных проектов, по крайней мере на предварительной версии JDK 9, для решения возникающих проблем может потребоваться изучение кода Java Packager.

На проходящих 4 апреля (JBreak 2017 в Новосибирске) и 7-8 апреля (JPoint 2017 в Москве) конференциях можно будет послушать доклады о других интересных возможностях Java 9 и следующих версий Java:

Поделиться с друзьями
-->

Комментарии (6)


  1. igor_suhorukov
    31.03.2017 10:29
    +2

    Спасибо за простой и понятный пример. Шикарно что все про что писали есть в виде maven сборки!

    Жаль что JEP 295 для AOT пока нельзя применить(и это не его цель на данном этапе) для создания своей бинарной сборки приложения. Размеры дистрибутива и время запуска приложения были бы еще меньше!


  1. dbelob
    31.03.2017 11:05
    +2

    Спасибо за простой и понятный пример. Шикарно что все про что писали есть в виде maven сборки!

    Спасибо! Постарался максимально упростить пример. В процессе пришлось изучать код Java Packager (даже не один билд).

    Жаль что JEP 295 для AOT пока нельзя применить

    Кстати, на эту тему будет доклад Дмитрия Чуйко «Преждевременная» компиляция — это нормально? (в Новосибирске) и недавно на JUG.ru был его же двухчасовой доклад Ahead-of-Time компиляция для HotSpot JVM (я смотрел видео, но не помню, упоминал ли он про поддержку модульности).


    1. igor_suhorukov
      31.03.2017 17:22
      +1

      Респект, в свежих сборках сложно обойтись без ковыряния в исходниках! Да, в докладе Дмитрия он говорил про jigsaw и загрузку so в linux 86x64 для пакетов грааля и java.base


      1. dbelob
        31.03.2017 17:42
        +1

        … то есть в оракловском AOT модульность поддерживается, как и предполагалось.


  1. f0stergas
    31.03.2017 17:37
    +1

    Спасибо за статью. Вопрос может касается только косвенно, но интересно: если я правильно понимаю, в Java 9 теперь class-loader теперь будет загружать только классы, которые указаны в зависимостях, а не весь rt.jar, что ускорит запуск приложений?


    1. dbelob
      31.03.2017 17:56
      +1

      В Java 9 появляется возможность включить в дистрибутив только те модули (содержащие только те классы), которые приложение использует.

      Файл rt.jar и в JRE, и в JDK теперь отсутствует вообще. Вместо него в каталоге lib имеется файл modules. В JRE и JDK он содержит все модули (все классы). При сборке собственного дистрибутива он создаётся и содержит только используемые приложением модули.