Пакет java.lang.invoke
java.lang.invoke предоставляет низкоуровневые примитивы для взаимодействия с виртуальной машиной Java. Как описано в Спецификации виртуальной машины Java, некоторые типы в этом пакете обрабатываются виртуальной машиной особым образом:
- Классы
MethodHandleиVarHandleсодержат полиморфные по сигнатуре методы, которые могут быть связаны независимо от их дескриптора типа. Обычно для связывания метода требуется точное совпадение дескрипторов типов. - Формат байт-кода JVM поддерживает непосредственные константы классов
MethodHandleиMethodType. - Инструкция
invokedynamicиспользует загрузочные константыMethodHandleдля динамического разрешения объектовCallSite, задающих пользовательское поведение вызова метода. - Инструкция
ldcиспользует загрузочные константыMethodHandleдля динамического разрешения пользовательских значений констант.
Динамическое разрешение точек вызова и констант
Ниже приведены низкоуровневые сведения, обобщающие относящиеся к делу разделы спецификации виртуальной машины Java. Полную информацию см. в актуальной версии этой спецификации.Динамически вычисляемые точки вызова
Инструкцияinvokedynamic изначально находится в несвязанном состоянии. В этом состоянии у инструкции нет целевого метода для вызова. Прежде чем JVM сможет выполнить инструкцию invokedynamic, инструкцию необходимо сначала связать. Связывание выполняется вызовом загрузочного метода, которому передаются статические сведения о вызове и который должен создать CallSite, задающий поведение вызова.
Каждая инструкция invokedynamic статически указывает собственный загрузочный метод в виде ссылки на запись пула констант. Ссылка на запись пула констант также задаёт имя вызова и дескриптор типа метода, как и invokestatic и другие инструкции вызова.
Динамически вычисляемые константы
Пул констант может содержать константы с тегомCONSTANT_Dynamic, снабжённые загрузочными методами, выполняющими их разрешение. Такая динамическая константа изначально находится в неразрешённом состоянии. Прежде чем JVM сможет использовать динамически вычисляемую константу, её необходимо сначала разрешить. Разрешение динамически вычисляемой константы выполняется вызовом загрузочного метода, которому передаются статические сведения о константе и который должен создать значение типа, статически объявленного для этой константы. Каждая динамически вычисляемая константа статически указывает собственный загрузочный метод в виде ссылки на запись пула констант. Ссылка на запись пула констант также задаёт имя константы и дескриптор типа поля, как и getstatic и другие инструкции обращения к полям. (Грубо говоря, динамически вычисляемая константа относится к динамически вычисляемой точке вызова так же, как CONSTANT_Fieldref относится к CONSTANT_Methodref.)
Выполнение загрузочных методов
Разрешение динамически вычисляемой точки вызова или константы начинается с разрешения следующих элементов из пула констант:- загрузочного метода,
CONSTANT_MethodHandle ClassилиMethodType, полученного из компонента типа дескриптораCONSTANT_NameAndType- статических аргументов, если они есть (обратите внимание, что статические аргументы сами могут быть динамически вычисляемыми константами)
Затем загрузочный метод вызывается так, как если бы был вызван MethodHandle.invoke, со следующими аргументами:
MethodHandles.Lookup— объект поиска для класса-вызывателя, в котором находится динамически вычисляемая константа или точка вызоваString— имя, указанное вCONSTANT_NameAndTypeMethodTypeилиClass— разрешённый дескриптор типа дляCONSTANT_NameAndTypeClass— разрешённый дескриптор типа константы, если это динамическая константа- дополнительные разрешённые статические аргументы, если они есть
Для динамически вычисляемой точки вызова возвращаемый результат должен быть ненулевой ссылкой на CallSite. Тип целевого объекта точки вызова должен точно совпадать с типом, полученным из дескриптора типа вызова и переданным загрузочному методу. Если эти условия не выполнены, выбрасывается BootstrapMethodError. В случае успеха точка вызова навсегда связывается с инструкцией invokedynamic.
Для динамически вычисляемой константы первый параметр загрузочного метода должен быть совместим с присваиванием к MethodHandles.Lookup. Если это условие не выполнено, выбрасывается BootstrapMethodError. В случае успеха результат загрузочного метода кэшируется как разрешённое значение константы.
Если во время выполнения загрузочного метода возникает исключение, например E, разрешение завершается неудачно и аварийно прекращается. Исключение E повторно выбрасывается, если тип E — это Error или его подкласс; в противном случае выбрасывается BootstrapMethodError, оборачивающий E. Если это происходит, при всех последующих попытках выполнить инструкцию invokedynamic или загрузить динамически вычисляемую константу будет выброшена та же ошибка.
Момент разрешения
Инструкцияinvokedynamic связывается непосредственно перед первым выполнением. Динамически вычисляемая константа разрешается непосредственно перед первым использованием (помещением в стек или связыванием в качестве параметра загрузочного метода). Вызов загрузочного метода, выполняющий связывание, происходит в потоке, который пытается выполнить инструкцию или использовать константу впервые. Если таких потоков несколько, загрузочный метод может быть вызван одновременно в нескольких потоках. Поэтому загрузочные методы, обращающиеся к глобальным данным приложения, должны принимать обычные меры предосторожности против состояний гонки. В любом случае каждая инструкция invokedynamic либо не связана, либо связана с уникальным объектом CallSite.
В приложении, которому требуются инструкции invokedynamic с индивидуально изменяемым поведением, загрузочные методы должны создавать отдельные объекты CallSite для каждого запроса на связывание. В качестве альтернативы приложение может связать один объект CallSite с несколькими инструкциями invokedynamic; в этом случае изменение целевого метода станет видимым для каждой из этих инструкций.
Если несколько потоков одновременно выполняют загрузочный метод для одной динамически вычисляемой точки вызова или константы, JVM должна выбрать результат одного загрузочного метода и сделать его видимым для всех потоков. Всем остальным вызовам загрузочного метода разрешено завершиться, но их результаты игнорируются.
Обсуждение: Эти правила не позволяют JVM совместно использовать точки вызова или выполнять «беспричинные» вызовы загрузочных методов. Каждая инструкция invokedynamic не более одного раза переходит из несвязанного состояния в связанное непосредственно перед первым вызовом. Отменить результат завершённого вызова загрузочного метода невозможно.
Типы загрузочных методов
Для динамически вычисляемой точки вызова загрузочный метод вызывается с параметрами типовMethodHandles.Lookup, String, MethodType и типов любых статических аргументов; возвращаемый тип — CallSite. Для динамически вычисляемой константы загрузочный метод вызывается с параметрами типов MethodHandles.Lookup, String, Class и типов любых статических аргументов; возвращаемый тип — это тип, представленный Class.
Поскольку MethodHandle.invoke допускает адаптацию между типом вызываемого метода и типом метода дескриптора загрузочного метода, объявление загрузочного метода может быть гибким. Для динамически вычисляемой константы первый тип параметра дескриптора загрузочного метода должен допускать присваивание к MethodHandles.Lookup; помимо этого ограничения, к загрузочным методам динамически вычисляемых точек вызова и динамически вычисляемых констант применима одинаковая степень гибкости. Примечание: это ограничение допускает возможность будущего вызова загрузочного метода только с типами параметров статических аргументов, что позволит использовать более широкий диапазон методов, совместимых со статическими аргументами (например, методов, которые не объявляют и не требуют параметры метаданных поиска, имени и типа).
Например, для динамически вычисляемой точки вызова первым аргументом может быть Object вместо MethodHandles.Lookup, а возвращаемым типом также может быть Object вместо CallSite. (Обратите внимание, что типы и количество аргументов в стеке ограничивают допустимые виды загрузочных методов статическими методами и конструкторами с соответствующими типами.)
Если помещаемое значение имеет примитивный тип, его можно преобразовать в ссылку с помощью упаковки. Если загрузочный метод является методом с переменным числом аргументов (установлен его бит-модификатор 0x0080), некоторые или все указанные здесь аргументы могут быть собраны в завершающий параметр-массив. (Это не специальное правило, а полезное следствие взаимодействия между константами CONSTANT_MethodHandle, битом-модификатором методов с переменным числом аргументов и преобразованием asVarargsCollector.)
С учётом этих правил ниже приведены примеры допустимых объявлений загрузочных методов для динамически вычисляемых точек вызова с различным количеством N дополнительных аргументов. Первая строка (обозначенная *) подходит для любого количества дополнительных аргументов.
| N | Пример загрузочного метода |
|---|---|
| * |
|
| 0 |
|
| 1 | CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)
|
| 2 |
|
String и Integer (или int) соответственно. В предпоследнем примере предполагается, что все дополнительные аргументы имеют тип String. Остальные примеры подходят для дополнительных аргументов любых типов. Обратите внимание, что все примеры, кроме второго и третьего, также подходят для динамически вычисляемых констант, если изменить возвращаемый тип так, чтобы он соответствовал объявленному типу константы (например, Object, который всегда совместим). Поскольку динамически вычисляемые константы могут передаваться загрузочным методам в качестве статических аргументов, ограничений на типы загрузочных аргументов нет. Однако аргументы типов boolean, byte, short или char нельзя непосредственно передать через записи пула констант CONSTANT_Integer, поскольку преобразования asType не выполняют необходимые сужающие преобразования примитивных типов.
В приведённых выше примерах возвращаемый тип всегда CallSite, но это не является обязательным свойством загрузочных методов. В случае динамически вычисляемой точки вызова требуется лишь, чтобы возвращаемый тип загрузочного метода можно было преобразовать (с помощью преобразований asType) в CallSite; это означает, что возвращаемый тип загрузочного метода может быть Object или ConstantCallSite. В случае динамически разрешаемой константы возвращаемый тип загрузочного метода должен преобразовываться в тип константы, представленный дескриптором её типа поля. Например, если динамическая константа имеет дескриптор типа поля "C" (char), возвращаемым типом загрузочного метода может быть Object, Character или char, но не int или Integer.
- С версии:
- 1.7
| Класс | Описание |
|---|---|
| CallSite | |
| ConstantBootstraps | Методы начальной загрузки для динамически вычисляемых констант. |
| ConstantCallSite | ConstantCallSite — это CallSite, целевой объект которого является постоянным и никогда не может быть изменён. |
| LambdaConversionException | LambdaConversionException |
| LambdaMetafactory | Методы, упрощающие создание простых «функциональных объектов», которые реализуют один или несколько интерфейсов, делегируя вызовы предоставленному MethodHandle, возможно, после адаптации типов и частичного вычисления аргументов. |
| MethodHandle | Дескриптор метода — это типизированная непосредственно исполняемая ссылка на базовый метод, конструктор, поле или аналогичную низкоуровневую операцию, с возможностью преобразования аргументов или возвращаемых значений. |
| MethodHandleInfo | Символическая ссылка, полученная путём разложения прямого дескриптора метода на составляющие его символические части. |
| MethodHandleProxies | Этот класс состоит исключительно из статических методов, помогающих адаптировать дескрипторы методов к другим типам JVM, например к интерфейсам. |
| MethodHandles | Этот класс состоит исключительно из статических методов, которые работают с дескрипторами методов или возвращают их. |
| MethodHandles.Lookup | Объект поиска — это фабрика для создания дескрипторов методов, если при создании требуется проверка доступа. |
| MethodHandles.Lookup.ClassOption | Набор параметров класса, определяющих, добавляется ли динамически скрытый класс, созданный методом Lookup::defineHiddenClass, в качестве нового члена к гнезду класса поиска и/или имеет ли скрытый класс сильную связь с загрузчиком классов, указанным как его определяющий загрузчик. |
| MethodType | Тип метода представляет аргументы и тип возвращаемого значения, принимаемые и возвращаемые дескриптором метода, либо аргументы и тип возвращаемого значения, передаваемые вызывающим кодом дескриптора метода и ожидаемые от него. |
| MutableCallSite | MutableCallSite — это CallSite, целевая переменная которого ведёт себя как обычное поле. |
| SerializedLambda | Сериализованная форма лямбда-выражения. |
| StringConcatException | StringConcatException выбрасывается классом StringConcatFactory при нарушении инвариантов связывания. |
| StringConcatFactory | Методы, упрощающие создание методов конкатенации строк, которые можно использовать для эффективного объединения известного количества аргументов известных типов, возможно, после адаптации типов и частичного вычисления аргументов. |
| SwitchPoint | SwitchPoint — это объект, который может сообщать о переходах состояния другим потокам. |
| TypeDescriptor | Сущность, имеющая дескриптор типа. |
| TypeDescriptor.OfField<F extends TypeDescriptor.OfField<F>> | Сущность, имеющая дескриптор типа поля. |
|
TypeDescriptor.OfMethod<F extends TypeDescriptor.OfField<F>, M extends TypeDescriptor.OfMethod<F, |
Сущность, имеющая дескриптор типа метода. Дескрипторы методов, соответствующие разделу JVMS 4.3.3, могут быть представлены номинально с помощью MethodType::describeConstable; в противном случае их невозможно представить номинально. |
| VarHandle | VarHandle — это динамически строго типизированная ссылка на переменную или на параметрически определённое семейство переменных, включая статические поля, нестатические поля, элементы массивов или компоненты структуры данных вне кучи. |
| VarHandle.AccessMode | Набор режимов доступа, определяющих, как осуществляется доступ к переменной, на которую ссылается VarHandle. |
| VarHandle.VarHandleDesc | Номинальный дескриптор константы VarHandle. |
| VolatileCallSite | VolatileCallSite — это CallSite, целевой объект которого действует как volatile-переменная. |
| WrongMethodTypeException | Выбрасывается, чтобы указать, что код попытался вызвать дескриптор метода с неправильным типом метода. |
© 1993, 2025, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.
https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/invoke/package-summary.html