openjdk.ruOpenJDK на русском

JEP 309: Dynamic Class-File Constants

Динамические константы class-файлов

АвторBrian Goetz
ОтветственныйLois Foltan
ТипFeature
ОбластьSE
СтатусClosed / Delivered
Выпуск11
Компонентhotspot / runtime
Обсуждениеamber dash dev at openjdk dot java dot net
ТрудоёмкостьM
ДлительностьM
Связан сJEP 303: Intrinsics for the LDC and INVOKEDYNAMIC Instructions
РецензентыMark Reinhold
ОдобренMark Reinhold
Создан2017/03/20 20:26
Обновлён2018/09/10 18:57
Задача8177279

Аннотация

Расширить формат class-файлов Java для поддержки новой формы пула констант, CONSTANT_Dynamic. При загрузке CONSTANT_Dynamic создание будет делегироваться bootstrap-методу, точно так же как при связывании точки вызова invokedynamic связывание делегируется bootstrap-методу.

Цели

Мы стремимся снизить затраты и сбои, связанные с созданием новых форм материализуемых констант class-файлов, что, в свою очередь, даёт разработчикам языков и авторам компиляторов более широкие возможности для выразительности и производительности. Для этого мы создаём одну новую форму пула констант, которую можно параметризовать поведением, заданным пользователем, в виде bootstrap-метода со статическими аргументами.

Мы также скорректируем протокол взаимодействия между JVM и bootstrap-методами на этапе связывания, чтобы bootstrap API, используемый invokedynamic, применялся и к динамическим константам.

С учётом опыта использования invokedynamic мы внесём изменения в протокол начальной загрузки как для invokedynamic, так и для динамических констант, ослабив некоторые ограничения на обработку списков аргументов bootstrap-методов.

Эта работа требует прототипирования поддержки в библиотеке JDK для репрезентативной выборки из нескольких видов типов констант, в частности variable handles (JEP 193). Для поддержки такого прототипирования эта работа будет согласована с другой работой над базовой поддержкой константных выражений в языке (JEP 303).

Что не является целью

Этот JEP направлен на поддержку произвольных констант в пуле констант. Хотя существуют предложения и о других применениях bootstrap-методов, например рецепты методов, этот JEP сосредоточен на одном применении.

Успех этого JEP не зависит от поддержки со стороны языка Java или бэкенда компилятора Java, хотя шансы на успех выше, если бэкенд компилятора будет его использовать.

Хотя большие составные константы — слабое место стратегии трансляции Java, этот JEP не может решить задачу для составных констант, пока не появятся лучшие способы инкапсулировать их в формы констант, например замороженные массивы или списки, специализированные для примитивных типов.

Критерии успеха

Как минимальное требование, должно быть практически возможно предоставить формы пула констант для описания зеркал примитивных классов (int.class), констант null, enum и большинства форм VarHandle через CONSTANT_Dynamic.

Динамические константы должны быть применимы в любом контексте, где сейчас допускаются обычные константы пула констант, например CONSTANT_String и CONSTANT_MethodType. Следовательно, они должны быть допустимыми операндами инструкции ldc и должны допускаться в качестве статических параметров bootstrap-методов.

Протокол взаимодействия с bootstrap-методами должен поддерживать сложные константы, содержащие тысячи аргументов-компонентов, снимая текущее ограничение в 251 константный аргумент. В качестве амбициозной цели также должен появиться способ, позволяющий bootstrap-методу точнее управлять ошибками связывания, возникающими при разрешении аргументов bootstrap-метода.

По завершении работы у нас также должны появиться основания полагать, что этот механизм можно заставить работать для широкого круга библиотечных типов, таких как производные method handles, небольшие неизменяемые коллекции (списки, отображения, множества), числовые типы, регулярные выражения, форматировщики строк или простые классы данных.

Последующая работа должна быть определена и задокументирована. См. «Возможные расширения» ниже.

Мотивация

Раздел 4.4 Java Virtual Machine Specification (спецификация виртуальной машины Java) описывает формат пула констант. Добавление новых форм пула констант, например поддержки MethodHandle и MethodType, появившейся в Java 7, — серьёзная задача, и она расходится волнами по всей экосистеме, поскольку затрагивает весь код, который разбирает или интерпретирует class-файлы. Из-за этого планка для создания новых форм пула констант очень высока.

С появлением invokedynamic ценность хранения сложных данных в пуле констант многократно возрастает, поскольку список статических аргументов bootstrap-метода invokedynamic — это последовательность констант. Разработчики протоколов invokedynamic (таких как LambdaMetafactory, добавленный в Java 8) постоянно сталкиваются с необходимостью кодировать поведение через существующий набор констант, что, в свою очередь, требует дополнительной, подверженной ошибкам логики проверки и извлечения в самом bootstrap-методе. Более богатые, более гибкие и более строго типизированные константы устраняют трения при разработке протоколов invokedynamic, что, в свою очередь, облегчает перенос сложной логики со времени выполнения на время связывания, повышая производительность программ и упрощая логику компилятора.

Описание

Точно так же, как связывание точки вызова invokedynamic включает обратный вызов из JVM в логику связывания на Java, этот же приём можно применить к разрешению элемента пула констант. Элемент пула констант CONSTANT_Dynamic кодирует bootstrap-метод, выполняющий разрешение (MethodHandle), тип константы (Class) и все статические аргументы bootstrap-метода (произвольную последовательность констант, за исключением циклов в пуле констант между динамическими константами.)

Мы добавляем новую форму пула констант, CONSTANT_Dynamic (новый тег константы 17), у которой после байта тега идут два компонента: индекс bootstrap-метода в том же формате, что и индекс в CONSTANT_InvokeDynamic, и CONSTANT_NameAndType, кодирующий ожидаемый тип.

С точки зрения поведения константа CONSTANT_Dynamic разрешается выполнением её bootstrap-метода со следующими параметрами: 1. локальный объект Lookup, 2. String, представляющий компонент имени константы, 3. Class, представляющий ожидаемый тип константы, и 4. все остальные аргументы bootstrap-метода. Как и в случае invokedynamic, за разрешение могут соревноваться несколько потоков, но будет выбран единственный победитель, а все остальные конкурирующие ответы будут отброшены. Вместо того чтобы возвращать объект CallSite, как того требует инструкция invokedynamic, bootstrap-метод будет возвращать значение, которое немедленно будет преобразовано к требуемому типу.

Как и в случае invokedynamic, компонент имени — это дополнительный, помимо типа, канал передачи информации о выражении bootstrap-методу. Ожидается, что так же, как инструкции invokedynamic находят применение компоненту имени (например, имя метода или какой-либо специальный дескриптор), динамические константы тоже найдут применение имени (например, имя константы enum или написание символьной константы). Размещение CONSTANT_NameAndType в обоих местах делает дизайн более единообразным. По сути, константы CONSTANT_Methodref и CONSTANT_Fieldref используются для ссылок на именованные члены классов, а аналогичные константы CONSTANT_InvokeDynamic и CONSTANT_Dynamic — для ссылок на именованные сущности с bootstrap-методами, запрограммированными пользователем.

Компонент типа константы, как для invokedynamic, так и для CONSTANT_Dynamic, определяет фактический тип точки вызова или константы (соответственно). Bootstrap-метод не дополняет и не ограничивает эту информацию о типе, поэтому bootstrap-методы могут быть (и часто бывают) слабо типизированными, тогда как сами байт-коды всегда строго типизированы.

Чтобы ослабить ограничения на длину спецификаторов bootstrap-методов, формулировки, определяющие вызов bootstrap-методов, будут скорректированы (с полной обратной совместимостью), чтобы bootstrap-методы с переменным числом аргументов (ACC_VARARGS) могли поглощать своими завершающими аргументами все оставшиеся статические аргументы, даже если их 2^16-1. (Формат class-файлов уже это допускает, хотя прочитать слишком длинные списки аргументов bootstrap-метода никак нельзя.) Для согласованности методы invokeWithArguments класса MethodHandle также будут расширены таким образом, если целевой метод имеет переменное число аргументов. Так вызов bootstrap-методов можно будет специфицировать через слабо типизированные методы invokeWithArguments и invoke, так же как сегодня он специфицирован только через invoke.

Управление ошибками связывания bootstrap-методов оказалось постоянным источником ошибок и RFE от пользователей invokedynamic, и эта тенденция, вероятно, усилится по мере усложнения bootstrap-методов (а с динамическими константами они неизбежно усложнятся). Если мы найдём способ дать bootstrap-методам более полный контроль над исключениями и это можно будет сделать просто, мы рассмотрим возможность реализовать это в рамках данного JEP. В противном случае это попадёт в список будущих улучшений.

Черновик спецификации Java Virtual Machine для CONSTANT_Dynamic можно найти в JDK-8189199 — задаче CSR, связанной с основной задачей разработки этого JEP.

Дальнейшая работа

Возможные будущие расширения:

  • Поддержка крупномасштабных констант, таких как массивы или таблицы ресурсов
  • Дальнейшие изменения протокола взаимодействия с bootstrap-методами
  • Другие применения bootstrap-методов, которые могут хорошо сочетаться с динамическими константами
  • Привязка динамических констант к атрибуту ConstantValue статических полей
  • Выведение ленивой инициализации констант на уровень языка Java
  • Интеграция новых констант со специальными правилами языка Java для константных выражений

Обсуждение проектных решений можно найти в JDK-8161256, где рассматривается ряд связанных RFE. Настоящий JEP выделен из этого более широкого списка возможностей.

Альтернативы

Многие применения CONSTANT_Dynamic можно заменить эквивалентными вызовами invokedynamic. (Такой вызов не принимал бы аргументов и был бы привязан к method handle, возвращающему нужную константу.) Однако такой обходной путь не помогает выполнить ключевое требование — возможность передавать синтетические константы в качестве аргументов bootstrap-метода.

Ещё одна альтернатива CONSTANT_Dynamic — использовать поля static final для именования нужных констант и вычислять их значения в статическом инициализаторе (<clinit>). Этот подход требует дополнительных метаданных (одноразового определения поля на каждую константу) и недостаточно ленив, чтобы избежать проблем с циклами при начальной загрузке. Эти проблемы обычно решаются созданием закрытых вложенных классов с независимыми статическими инициализаторами, но это тоже требует дополнительных метаданных. Если языки будут развиваться в сторону использования множества таких констант, избыточные метаданные приведут к разбуханию приложений.

Другой подход — генерировать статические методы, выполняющие логику построения константы, а затем лениво вызывать их из invokedynamic. Опять же, такие одноразовые методы — это накладные расходы на метаданные, большие по сравнению с CONSTANT_Dynamic.

На практике накладные расходы на метаданные при имитации этих возможностей слишком велики.

Зависимости

Эта возможность ориентирована на JVM и поэтому не зависит от более высоких программных уровней.

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

Как и в случае invokedynamic, для широкого распространения требуется использование в бэкенде javac, что, в свою очередь, может потребовать расширений языка. В качестве базового первого шага следует рассмотреть обходные решения трансляции, требующие скрытых статических методов, например трансляцию int.class или таблиц отображения для switch, и по возможности переформулировать их с использованием новых констант.