JEP 220: Modular Run-Time Images
Модульные образы среды выполнения
| Автор | Mark Reinhold |
| Ответственный | Alan Bateman |
| Тип | Feature |
| Область | SE |
| Статус | Closed / Delivered |
| Выпуск | 9 |
| JSR | 376 |
| Обсуждение | jigsaw dash dev at openjdk dot java dot net |
| Трудоёмкость | L |
| Длительность | L |
| Блокирует | JEP 200: The Modular JDK |
| JEP 261: Module System | |
| Связан с | JEP 162: Prepare for Modularization |
| JEP 201: Modular Source Code | |
| JEP 282: jlink: The Java Linker | |
| Рецензенты | Alan Bateman, Alex Buckley, Chris Hegarty, Mandy Chung, Paul Sandoz |
| Создан | 2014/10/23 15:05 |
| Обновлён | 2017/09/22 20:16 |
| Задача | 8061971 |
Аннотация
Изменить структуру образов среды выполнения JDK и JRE так, чтобы в них можно было разместить модули, и повысить их производительность, безопасность и удобство сопровождения. Определить новую схему URI для именования модулей, классов и ресурсов, хранящихся в образе среды выполнения, не раскрывая внутреннюю структуру или формат образа. При необходимости изменить существующие спецификации с учётом этих изменений.
Цели
-
Принять формат среды выполнения для хранимых файлов классов и ресурсов, который:
-
эффективнее по времени и по занимаемому месту, чем устаревший формат JAR, который, в свою очередь, основан на древнем формате ZIP;
-
позволяет находить и загружать файлы классов и ресурсов по отдельности для каждого модуля;
-
может хранить файлы классов и ресурсов из модулей JDK, а также из модулей библиотек и приложений;
-
может быть расширен в будущем для размещения дополнительных видов данных, например заранее вычисленных структур данных JVM и заранее скомпилированного нативного кода для классов Java.
-
-
Изменить структуру образов среды выполнения JDK и JRE так, чтобы провести чёткое различие между файлами, на которые могут полагаться разработчики, специалисты по развёртыванию и конечные пользователи и которые они при необходимости могут изменять, и файлами, которые относятся к внутренней реализации и могут измениться без предупреждения.
-
Предоставить поддерживаемые способы выполнять распространённые операции, такие как, например, перечисление всех классов, присутствующих в образе, для которых сегодня приходится изучать внутреннюю структуру образа среды выполнения.
-
Дать возможность выборочно лишать привилегий классы JDK, которым сегодня предоставлены все разрешения безопасности, хотя на самом деле эти разрешения им не нужны.
-
Сохранить существующее поведение корректно написанных приложений, т. е. приложений, которые не зависят от внутренних особенностей образов среды выполнения JRE и JDK.
Критерии успеха
Модульные образы среды выполнения, эквивалентные образам JRE, JDK и Compact Profile непосредственно предшествующей сборки JDK 9, не должны показывать ухудшения на репрезентативном наборе тестов производительности запуска, статического и динамического потребления памяти.
Что не является целью
-
Сохранение всех особенностей текущей структуры образа среды выполнения не является целью.
-
Точное сохранение текущего поведения всех существующих API не является целью.
Мотивация
Проект Jigsaw ставит целью спроектировать и реализовать стандартную модульную систему для платформы Java SE и применить эту систему к самой платформе и к JDK. Его основные цели — упростить масштабирование реализаций платформы вплоть до небольших устройств, повысить безопасность и удобство сопровождения, обеспечить более высокую производительность приложений и дать разработчикам более удобные инструменты для разработки крупных программ.
Этот JEP — третий из четырёх JEP проекта Jigsaw. Более ранний JEP 200 определяет структуру модульного JDK, а JEP 201 реорганизует исходный код JDK в модули. Более поздний JEP 261 вводит саму модульную систему.
Описание
Текущая структура образа среды выполнения
Система сборки JDK сейчас создаёт два типа образов среды выполнения: Java Runtime Environment (JRE), полную реализацию платформы Java SE, и Java Development Kit (JDK), который включает JRE, а также инструменты и библиотеки для разработки. (Три сборки Compact Profile являются подмножествами JRE.)
Корневой каталог образа JRE содержит два каталога, bin и lib, со следующим содержимым:
-
Каталог
binсодержит основные исполняемые файлы, в частности командуjavaдля запуска среды выполнения. (В операционной системе Windows он также содержит динамически подключаемые нативные библиотеки среды выполнения.) -
Каталог
libсодержит различные файлы и подкаталоги:-
различные файлы
.propertiesи.policy, большинство из которых разработчики, специалисты по развёртыванию и конечные пользователи могут редактировать, хотя делают это редко; -
каталог
endorsed, который по умолчанию не существует и в который можно помещать JAR-файлы с реализациями одобренных стандартов и автономных технологий; -
каталог
ext, в который можно помещать JAR-файлы, содержащие расширения или дополнительные пакеты; -
различные внутренние файлы данных реализации в разнообразных двоичных форматах, например шрифты, цветовые профили и данные о часовых поясах;
-
различные JAR-файлы, включая
rt.jar, которые содержат файлы классов Java и ресурсов среды выполнения. -
Динамически подключаемые нативные библиотеки среды выполнения в операционных системах Linux, macOS и Solaris.
-
Образ JDK включает копию JRE в подкаталоге jre и содержит дополнительные подкаталоги:
-
каталог
binсодержит инструменты командной строки для разработки и отладки, напримерjavac,javadocиjconsole, а также для удобства копии исполняемых файлов из каталогаjre/bin; -
каталоги
demoиsampleсодержат соответственно демонстрационные программы и примеры кода; -
каталог
manсодержит справочные страницы в стиле UNIX; -
каталог
includeсодержит заголовочные файлы C/C++ для компиляции нативного кода, который напрямую взаимодействует со средой выполнения; -
каталог
libсодержит различные JAR-файлы и файлы других типов, из которых состоят реализации инструментов JDK, в том числеtools.jar, содержащий классы компилятораjavac.
Корневой каталог образа JDK или образа JRE, не встроенного в образ JDK, также содержит различные файлы COPYRIGHT, LICENSE и README, а также файл release, который описывает образ в виде простых пар свойств «ключ/значение», например:
JAVA_VERSION="1.9.0"
OS_NAME="Linux"
OS_VERSION="2.6"
OS_ARCH="amd64"
Новая структура образа среды выполнения
Нынешнее различие между образами JRE и JDK сложилось чисто исторически: это следствие решения о реализации, принятого на позднем этапе разработки выпуска JDK 1.2 и с тех пор не пересматривавшегося. Новая структура образа устраняет это различие: образ JDK — это просто образ среды выполнения, который содержит полный набор инструментов разработки и других компонентов, исторически входивших в JDK.
Модульный образ среды выполнения содержит следующие каталоги:
-
Каталог
binсодержит все средства запуска командной строки, определённые модулями, скомпонованными в образ. (В Windows он по-прежнему содержит динамически подключаемые нативные библиотеки среды выполнения.) -
Каталог
confсодержит файлы.properties,.policyи другие виды файлов, предназначенные для редактирования разработчиками, специалистами по развёртыванию и конечными пользователями, которые раньше находились в каталогеlibили его подкаталогах. -
Каталог
libв Linux, macOS и Solaris, как и сейчас, содержит динамически подключаемые нативные библиотеки среды выполнения. Эти файлы с именамиlibjvm.soилиlibjvm.dylibмогут подключаться программами, которые встраивают среду выполнения. Несколько других файлов в этом каталоге также предназначены для внешнего использования, в том числеsrc.zipиjexec. -
Все остальные файлы и каталоги в каталоге
libследует рассматривать как закрытые детали реализации среды выполнения. Они не предназначены для внешнего использования, а их имена, формат и содержимое могут измениться без предупреждения. -
Каталог
legalсодержит юридические уведомления для модулей, скомпонованных в образ, сгруппированные по одному подкаталогу на модуль. -
Полный образ JDK, как и сейчас, дополнительно содержит каталоги
demo,manиinclude. (Каталогsamplesбыл удалён в JEP 298.)
Корневой каталог модульного образа среды выполнения также содержит файл release, который генерирует система сборки. Чтобы было легко определить, какие модули присутствуют в образе среды выполнения, файл release включает новое свойство MODULES — список имён этих модулей через пробел. Список упорядочен топологически в соответствии с зависимостями между модулями, поэтому модуль java.base всегда идёт первым.
Удалено: механизм переопределения одобренных стандартов
Механизм переопределения одобренных стандартов позволял устанавливать в образ среды выполнения реализации более новых версий стандартов, которые сопровождаются вне Java Community Process, или автономных API, которые входят в платформу Java SE, но продолжают развиваться независимо.
Механизм одобренных стандартов был определён через системное свойство в виде пути, java.endorsed.dirs, и значение этого свойства по умолчанию, $JAVA_HOME/lib/endorsed. JAR-файл с более новой реализацией одобренного стандарта или автономного API можно установить в образ среды выполнения, поместив его в один из каталогов, указанных в системном свойстве, или в каталог по умолчанию lib/endorsed, если системное свойство не задано. Такие JAR-файлы во время выполнения добавляются в начало загрузочного пути классов JVM и тем самым переопределяют любые определения, хранящиеся в самой среде выполнения.
Модульный образ состоит из модулей, а не из JAR-файлов. В дальнейшем одобренные стандарты и автономные API поддерживаются только в модульной форме, через концепцию обновляемых модулей. Поэтому мы удалили механизм переопределения одобренных стандартов, включая системное свойство java.endorsed.dirs и каталог lib/endorsed. Чтобы помочь выявить существующие случаи использования этого механизма, компилятор и средство запуска теперь завершаются с ошибкой, если это системное свойство задано или если каталог lib/endorsed существует.
Удалено: механизм расширений
Механизм расширений позволял устанавливать в образ среды выполнения JAR-файлы с API, расширяющими платформу Java SE, так что их содержимое было видно каждому приложению, которое компилируется с этим образом или работает на нём.
Механизм был определён через системное свойство в виде пути, java.ext.dirs, и значение этого свойства по умолчанию, состоящее из $JAVA_HOME/lib/ext и общесистемного каталога, зависящего от платформы (например, /usr/java/packages/lib/ext в Linux). Он работал примерно так же, как механизм одобренных стандартов, за исключением того, что JAR-файлы, помещённые в каталог расширений, загружались загрузчиком классов расширений среды выполнения, который является дочерним по отношению к начальному загрузчику классов и родительским по отношению к системному загрузчику классов, который и загружает запускаемое приложение из пути классов. Поэтому классы расширений не могли переопределять классы JDK, загружаемые начальным загрузчиком, но загружались с приоритетом перед классами, определёнными системным загрузчиком и его потомками.
Механизм расширений появился в JDK 1.2, выпущенном в 1998 году, но в наше время мы видим мало свидетельств его использования. Это неудивительно, поскольку большинство приложений Java сегодня помещают нужные им библиотеки прямо в путь классов, а не требуют устанавливать эти библиотеки как расширения среды выполнения.
Технически можно, хотя и неудобно, продолжать поддерживать механизм расширений в модульном JDK. Чтобы упростить и платформу Java SE, и JDK, мы удалили механизм расширений, включая системное свойство java.ext.dirs и каталог lib/ext. Чтобы помочь выявить существующие случаи использования этого механизма, компилятор и средство запуска теперь завершаются с ошибкой, если это системное свойство задано или если каталог lib/ext существует. По умолчанию компилятор и средство запуска игнорируют общесистемный каталог расширений, зависящий от платформы, но если указан параметр командной строки -XX:+CheckEndorsedAndExtDirs, они завершаются с ошибкой, если этот каталог существует и не пуст.
Несколько возможностей, связанных с механизмом расширений, были сохранены, поскольку они полезны сами по себе:
-
атрибут манифеста
Class-Path, который указывает JAR-файлы, необходимые другому JAR-файлу; -
атрибуты манифеста
{Specification,Implementation}-{Title,Version,Vendor}, которые задают сведения о версии пакета и JAR-файла; -
атрибут манифеста
Sealed, который запечатывает пакет или JAR-файл; -
сам загрузчик классов расширений, хотя теперь он называется платформенным загрузчиком классов.
Удалено: rt.jar и tools.jar
Файлы классов и ресурсов, которые раньше хранились в lib/rt.jar, lib/tools.jar, lib/dt.jar и различных других внутренних JAR-файлах, теперь хранятся в более эффективном формате в файлах, зависящих от реализации, в каталоге lib. Формат этих файлов не специфицирован и может измениться без уведомления.
Удаление rt.jar и подобных файлов приводит к трём разным проблемам:
-
Существующие стандартные API, такие как метод
ClassLoader::getSystemResource, возвращают объектыURL, которые обозначают файлы классов и ресурсов внутри образа среды выполнения. Например, при запуске в JDK 8 кодClassLoader.getSystemResource("java/lang/Class.class");возвращает URL
jarвидаjar:file:/usr/local/jdk8/jre/lib/rt.jar!/java/lang/Class.classкоторый, как видно, содержит вложенный URL
file, обозначающий сам JAR-файл внутри образа среды выполнения. С помощью методаgetContentэтого объектаURLможно получить содержимое файла класса через встроенный обработчик протокола для схемы URLjar.Модульный образ не содержит JAR-файлов, поэтому URL такого вида не имеют смысла. К счастью, спецификации
getSystemResourceи связанных методов не требуют, чтобы возвращаемые этими методами объектыURLдействительно использовали схему JAR. Однако они требуют, чтобы через эти объектыURLможно было загрузить содержимое хранимого файла класса или ресурса. -
API
java.security.CodeSourceи файлы политики безопасности используют URL для обозначения расположения баз кода, которым нужно предоставить указанные разрешения. Компоненты системы выполнения, которым нужны определённые разрешения, сейчас обозначаются в файлеlib/security/java.policyс помощью URLfile. Провайдер криптографии на эллиптических кривых, например, обозначается какfile:${java.home}/lib/ext/sunec.jarчто, очевидно, не имеет смысла в модульном образе.
-
IDE и другим инструментам разработки нужна возможность перечислять файлы классов и ресурсов, хранящиеся в образе среды выполнения, и читать их содержимое. Сегодня они часто делают это напрямую, открывая и читая
rt.jarи подобные файлы. С модульным образом это, конечно, невозможно.
Новая схема URI для обозначения хранимых модулей, классов и ресурсов
Для решения трёх описанных выше проблем можно использовать новую схему URL, jrt, чтобы обозначать модули, классы и ресурсы, хранящиеся в образе среды выполнения, не раскрывая внутреннюю структуру или формат образа.
URL jrt — это иерархический URI в соответствии с RFC 3986 со следующим синтаксисом
jrt:/[$MODULE[/$PATH]]
где $MODULE — необязательное имя модуля, а $PATH, если присутствует, — путь к конкретному файлу класса или ресурса внутри этого модуля. Значение URL jrt зависит от его структуры:
-
jrt:/$MODULE/$PATHобозначает конкретный файл класса или ресурса с именем$PATHвнутри заданного$MODULE. -
jrt:/$MODULEобозначает все файлы классов и ресурсов в модуле$MODULE. -
jrt:/обозначает всю совокупность файлов классов и ресурсов, хранящихся в текущем образе среды выполнения.
Эти три формы URL jrt решают описанные выше проблемы следующим образом:
-
API, которые сейчас возвращают URL
jar, теперь возвращают URLjrt. Приведённый выше вызовClassLoader::getSystemResource, например, теперь возвращает URLjrt:/java.base/java/lang/Class.classВстроенный обработчик протокола для схемы
jrtгарантирует, что методgetContentтаких объектовURLполучает содержимое указанного файла класса или ресурса. -
Файлы политики безопасности и другие места, где используется API
CodeSource, могут с помощью URLjrtобозначать конкретные модули, чтобы предоставлять им разрешения. Провайдер криптографии на эллиптических кривых, например, теперь можно обозначить URLjrtjrt:/jdk.crypto.ecДругие модули, которым сейчас предоставлены все разрешения, но которым они на самом деле не нужны, можно без труда лишить привилегий, т. е. дать им ровно те разрешения, которые им нужны.
-
Встроенный провайдер NIO FileSystem для схемы URL
jrtгарантирует, что инструменты разработки смогут перечислять и читать файлы классов и ресурсов в образе среды выполнения, загрузив FileSystem, обозначенную URLjrt:/, следующим образом:FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); byte[] jlo = Files.readAllBytes(fs.getPath("modules", "java.base", "java/lang/Object.class"));Каталог верхнего уровня
modulesв этой файловой системе содержит по одному подкаталогу для каждого модуля в образе. Каталог верхнего уровняpackagesсодержит по одному подкаталогу для каждого пакета в образе, а этот подкаталог содержит символическую ссылку на подкаталог модуля, который определяет этот пакет.Для инструментов, которые поддерживают разработку кода для JDK 9, но сами работают на JDK 8, копия этого провайдера файловой системы, пригодная для использования на JDK 8, помещается в каталог
libобразов среды выполнения JDK 9 в файл с именемjrt-fs.jar.
(Обработчик протокола URL jrt не возвращает никакого содержимого для URL второй и третьей форм.)
Изменения в системе сборки
Система сборки создаёт описанный выше новый формат образа среды выполнения с помощью компоновщика Java (JEP 282).
Мы воспользовались случаем, чтобы наконец переименовать каталоги images/j2sdk-image и images/j2re-image в images/jdk и images/jre соответственно.
Небольшие изменения спецификаций
JEP 162, реализованный в JDK 8, внёс ряд изменений, чтобы подготовить платформу Java SE и JDK к работе по модуляризации, описанной здесь и в связанных JEP. Среди этих изменений было удаление нормативных положений спецификаций, которые требуют искать определённые файлы конфигурации в каталоге lib образов среды выполнения, поскольку теперь эти файлы находятся в каталоге conf. Большинство API, относящихся только к SE и содержащих такие положения, были соответствующим образом пересмотрены в рамках Java SE 8, но некоторые API, общие для платформ Java SE и EE, всё ещё содержат такие положения:
-
javax.xml.stream.XMLInputFactoryуказывает${java.home}/lib/stax.properties(JSR 173). -
javax.xml.ws.spi.Providerуказывает${java.home}/lib/jaxws.properties(JSR 224). -
javax.xml.soap.MessageFactoryи связанные классы указывают${java.home}/lib/jaxm.properties(JSR 67).
В Java SE 9 эти положения больше не требуют использовать каталог lib.
Тестирование
Некоторые существующие тесты напрямую использовали внутреннее устройство образа среды выполнения (например, rt.jar) или обращались к системным свойствам (например, java.ext.dirs), которых больше нет. Эти тесты исправлены.
Сборки Early Access (ранний доступ) с описанными здесь изменениями были доступны на протяжении всей разработки системы модулей. Участникам Java-сообщества настоятельно рекомендовалось тестировать свои инструменты, библиотеки и приложения на этих сборках, чтобы помочь выявить проблемы совместимости.
Риски и допущения
Основные риски этого предложения связаны с совместимостью и сводятся к следующему:
-
Как отмечено выше, образ JDK больше не содержит подкаталог
jre. Существующий код, который предполагает наличие этого каталога, может работать некорректно. -
Как отмечено выше, образы JDK и JRE больше не содержат файлы
lib/rt.jar,lib/tools.jar,lib/dt.jarи другие внутренние JAR-файлы. Существующий код, который предполагает наличие этих файлов, может работать некорректно. -
Как отмечено выше, системные свойства
java.endorsed.dirsиjava.ext.dirsбольше не определены. Существующий код, который предполагает, что эти свойства имеют значения, отличные отnull, может работать некорректно. -
Динамически компонуемые нативные библиотеки системы выполнения всегда находятся в каталоге
lib, кроме Windows; в сборках для Linux и Solaris они раньше помещались в подкаталогlib/$ARCH. Это был пережиток образов, которые могли поддерживать несколько архитектур процессоров, а это больше не требуется. -
Файл
src.zipтеперь находится в каталогеlib, а не в каталоге верхнего уровня, и теперь содержит по одному каталогу для каждого модуля в образе. IDE и другие инструменты, которые читают этот файл, потребуется обновить. -
Как отмечено выше, существующие стандартные API, которые возвращают объекты
URLдля обозначения файлов классов и ресурсов внутри образа среды выполнения, теперь возвращают URLjrt. Существующий код, который ожидает, что эти API возвращают URLjar, может работать некорректно. -
Внутреннее системное свойство
sun.boot.class.pathудалено. Существующий код, который зависит от этого свойства, может работать некорректно.
-
Файлы классов и ресурсов в образе JDK, которые раньше находились в
lib/tools.jarи были видны, только если этот файл добавлен в путь классов, теперь видны через системный загрузчик классов или, в некоторых случаях, через загрузчик классов начальной загрузки. Модули, содержащие эти файлы, не указаны в пути классов приложения, т. е. в значении системного свойстваjava.class.path. -
Файлы классов и ресурсов, которые раньше находились в
lib/dt.jarи были видны, только если этот файл добавлен в путь классов, теперь видны через загрузчик классов начальной загрузки и присутствуют и в JRE, и в JDK. -
Файлы конфигурации, которые раньше находились в каталоге
lib, включая файл политики безопасности, теперь расположены в каталогеconf. Существующий код, который анализирует или изменяет эти файлы, может потребоваться обновить. -
Изменился определяющий загрузчик классов для типов в некоторых существующих пакетах. Существующий код, который делает предположения о загрузчиках классов этих типов, может работать некорректно. Конкретные изменения перечислены в JEP 261. Некоторые из этих изменений — следствие того, как были разбиты на модули компоненты, содержащие и API, и инструменты. Классы такого компонента исторически были разделены между
rt.jarиtools.jar, но теперь все такие классы находятся в одном модуле. -
Каталог
binв образе JRE содержит несколько команд, которые раньше были только в образах JDK, а именноappletviewer,idlj,jrunscriptиjstatd. Как и в предыдущем пункте, эти изменения — следствие того, как были разбиты на модули компоненты, содержащие и API, и инструменты.
Зависимости
Этот JEP — третий из четырёх JEP проекта Jigsaw. Он зависит от JEP 201, в котором исходный код JDK был реорганизован в модули, а система сборки доработана для компиляции модулей. Он также зависит от более ранней подготовительной работы, выполненной в JEP 162, реализованном в JDK 8.