Пакет 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, разрешенный описатель типа константы, если это динамическая константа- дополнительные разрешенные статические аргументы, если таковые имеются
Для динамически вычисляемого места вызова возвращаемое значение должно быть ссылкой на не-null объект 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, помимо этого ограничения, та же степень гибкости применима к методам-инициализаторам динамически вычисляемых мест вызова и динамически вычисляемых констант. Примечание: это ограничение позволяет в будущем возможность вызова метода-инициализатора только с параметрами типов статических аргументов, тем самым поддерживая более широкий спектр методов, совместимых со статическими аргументами (например, методы, которые не объявляют или не требуют параметров метаданных lookup, имя и тип).
Например, для динамически вычисляемого места вызова первым аргументом может быть Object вместо MethodHandles.Lookup, а возвращаемый тип также может быть Object вместо CallSite. (Обратите внимание, что типы и количество стековых аргументов ограничивают законные типы методов-инициализаторов соответствующими статическими методами и конструкторами.)
Если приводимое значение является примитивным типом, оно может быть преобразовано в ссылку путём преобразования boxing. Если метод-инициализатор является методом с переменным числом аргументов (его битовая метка 0x0080 установлена), то некоторые или все аргументы, указанные здесь, могут быть собраны в параметр массива. (Это не специальное правило, а скорее полезное следствие взаимодействия между константами CONSTANT_MethodHandle, модификатором метода с переменным числом аргументов и преобразованием asVarargsCollector.)
Учитывая эти правила, вот примеры допустимых объявлений методов-инициализаторов для динамически вычисляемых мест вызова, учитывая различное число N дополнительных аргументов. Первая строка (помеченная *) будет работать для любого количества дополнительных аргументов.
| N | Образец метода bootstrap |
|---|---|
| * |
|
| 0 |
|
| 1 | CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)
|
| 2 |
|
String и Integer (или int) соответственно. В предпоследнем примере предполагается, что все дополнительные аргументы имеют тип String. Другие примеры работают со всеми типами дополнительных аргументов. Обратите внимание, что все примеры, кроме второго и третьего, также работают с динамически вычисляемыми константами, если тип возвращаемого значения изменён для совместимости с объявленным типом константы (например, Object, который всегда совместим). Поскольку динамически вычисляемые константы могут быть предоставлены в качестве статических аргументов методам bootstrap, нет ограничений на типы аргументов bootstrap. Однако аргументы типа boolean, byte, short или char не могут быть непосредственно предоставлены записями константного пула CONSTANT_Integer, так как преобразования asType не выполняют необходимые преобразования сужающих примитивов.
В приведенных выше примерах тип возвращаемого значения всегда CallSite, но это не является обязательным свойством методов bootstrap. В случае динамически вычисляемого вызова сайта единственным требованием является то, что тип возвращаемого значения метода bootstrap должен быть преобразуем (с помощью преобразований asType) к типу CallSite, что означает, что тип возвращаемого значения метода bootstrap может быть Object или ConstantCallSite. В случае динамически разрешённой константы тип возвращаемого значения метода bootstrap должен быть преобразуем к типу константы, как представлено её описателем типа поля. Например, если динамическая константа имеет описатель типа поля "C" (char), то тип возвращаемого значения метода bootstrap может быть Object, Character или char, но не int или Integer.
- С тех пор:
- 1.7
| Класс | Описание |
|---|---|
| CallSite | |
| ConstantBootstraps | Методы bootstrap для динамически вычисляемых констант. |
| ConstantCallSite | ConstantCallSite — это CallSite, целевой объект которого является постоянным и никогда не может быть изменён. |
| LambdaConversionException | LambdaConversionException |
| LambdaMetafactory | Методы для облегчения создания простых «функциональных объектов», которые реализуют один или несколько интерфейсов делегированием предоставленному MethodHandle, возможно, после адаптации типов и частичной оценки аргументов. |
| MethodHandle | Метод handle — это типизированная, непосредственно исполняемая ссылка на метод, конструктор, поле или аналогичную низкоуровневую операцию со свойствами, позволяющими преобразовать аргументы или значения возврата. |
| MethodHandleInfo | Символьная ссылка, полученная путём разбора прямого method handle на составляющие символические части. |
| MethodHandleProxies | Этот класс состоит исключительно из статических методов, которые помогают адаптировать method handle к другим типам JVM, таким как интерфейсы. |
| MethodHandles | Этот класс состоит исключительно из статических методов, которые работают с методами handle или возвращают их. |
| MethodHandles.Lookup | Объект lookup — это фабрика для создания method handle, когда для создания требуется проверка доступа. |
| MethodHandles.Lookup.ClassOption | Набор опций класса, которые определяют, добавляется ли скрытый класс, созданный методом Lookup::defineHiddenClass, динамически в качестве нового члена в гнездо класса lookup, и/или имеет ли скрытый класс сильную связь с загрузчиком классов, помеченным как определяющий загрузчик. |
| MethodType | Тип метода представляет аргументы и тип возврата, принимаемые и возвращаемые методом handle, или аргументы и тип возврата, передаваемые и ожидаемые вызывающим методом handle. |
| 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 | Выбрасывается для указания попытки вызвать method handle с неправильным типом метода. |
© 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://download.java.net/java/early_access/jdk24/docs/api/java.base/java/lang/invoke/package-summary.html