Package java.lang.invoke
java.lang.invoke
软件包提供了与Java虚拟机交互的低级基元。
如Java虚拟机规范中所述,此程序包中的某些类型由虚拟机进行特殊处理:
- 类
MethodHandle
VarHandle
包含signature polymorphic methods ,无论其类型描述符如何,都可以链接。 通常,方法链接需要类型描述符的精确匹配。 - JVM字节码格式支持类
MethodHandle
和MethodType
的立即常量。 -
invokedynamic
指令使用bootstrapMethodHandle
常量来动态解析CallSite
对象以获取自定义方法调用行为。 -
ldc
指令使用bootstrapMethodHandle
常量来动态解析自定义常量值。
呼叫站点和常量的动态分辨率
以下低级信息总结了Java虚拟机规范的相关部分。 有关完整详细信息,请参阅该规范的当前版本。动态计算的呼叫站点
invokedynamic
指令最初处于未链接状态。
在这种状态下,没有指令调用的目标方法。
在JVM可以执行invokedynamic
指令之前,必须首先链接指令。 链接是通过调用一个bootstrap方法来完成的,该方法被赋予调用的静态信息内容,并且必须产生一个给出调用行为的CallSite
。
每个invokedynamic
指令将其自己的引导方法静态指定为常量池引用。 常量池引用还指定了调用的名称和方法类型描述符,就像invokestatic
和其他调用指令一样。
动态计算常量
常量池可能包含标记为CONSTANT_Dynamic
常量,配有执行其分辨率的引导方法。
这种动态常数最初处于未解决状态。
在JVM可以使用动态计算常量之前,必须先解决它 。
动态计算的恒定分辨率是通过调用一个bootstrap方法来完成的,该方法给出了常量的静态信息内容,并且必须产生常量静态声明类型的值。
每个动态计算的常量静态地将其自己的引导方法指定为常量池引用。 常量池引用还指定常量的名称和字段类型描述符,就像getstatic
和其他字段引用指令一样。 (粗略地说,动态计算的常量是动态计算的呼叫站点,因为CONSTANT_Fieldref
是CONSTANT_Methodref
)
执行引导程序方法
解析动态计算的调用站点或常量首先从常量池中解析以下项目的常量:- 引导方法,
CONSTANT_MethodHandle
-
类
或MethodType
派生自CONSTANT_NameAndType
描述符的类型组件 - 静态参数,如果有的话(请注意,静态参数本身可以是动态计算的常量)
然后使用以下参数调用引导方法,如MethodHandle.invoke
所示 :
- a
MethodHandles.Lookup
,它是调用者类的查找对象,其中发生动态计算的常量或调用站点 - 一个
String
中所提到的,名字CONSTANT_NameAndType
- 一个
MethodType
或类
,已结案的类型描述CONSTANT_NameAndType
- a
类
,常量的已解析类型描述符,如果它是动态常量 - 其他已解析的静态参数(如果有)
对于动态计算的调用站点,返回的结果必须是对CallSite
的非空引用。 调用站点的目标类型必须与从调用的类型描述符派生的类型完全相同,并传递给bootstrap方法。 如果不满足这些条件,则抛出BootstrapMethodError
。 成功后,呼叫站点将永久链接到invokedynamic
指令。
对于动态计算的常量,bootstrap方法的第一个参数必须可分配给MethodHandles.Lookup
。 如果不满足此条件,则抛出BootstrapMethodError
。 成功时,bootstrap方法的结果将缓存为已解析的常量值。
如果在执行引导方法期间发生异常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
, 类
,和类型中的任何静态参数; 返回类型是类
表示的类型。
因为MethodHandle.invoke
允许在调用的方法类型和引导方法句柄的方法类型之间进行调整,所以引用方法的声明具有灵活性。 对于动态计算的常量,引导方法句柄的第一个参数类型必须可分配给MethodHandles.Lookup
,除了该约束之外,相同程度的灵活性适用于动态计算的调用站点和动态计算常量的引导方法。 注意:此约束允许将来仅使用静态参数的参数类型调用bootstrap方法的可能性,从而支持与静态参数兼容的更广泛的方法(例如不声明或需要查找的方法,名称和类型元数据参数)。
例如,对于动态计算的调用站点,第一个参数可以是Object
而不是MethodHandles.Lookup
,返回类型也可以是Object
而不是CallSite
。 (请注意,堆叠参数的类型和数量将合法类型的引导方法限制为适当键入的静态方法和构造函数。)
如果推送值是基本类型,则可以通过装箱转换将其转换为引用。 如果引导方法是变量arity方法(其修改0x0080
位0x0080
已设置),则此处指定的一些或所有参数可能被收集到尾随数组参数中。 (这不是一个特殊规则,而是CONSTANT_MethodHandle
常量之间相互作用的有用结果,变量arity方法的修饰符位和asVarargsCollector
转换。)
鉴于这些规则,以下是动态计算的调用站点的合法引导方法声明的示例,给出了额外参数的各种数字N
。 第一行(标记为*
)适用于任意数量的额外参数。
CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)
CallSite bootstrap(Object... args)
CallSite bootstrap(Object caller, Object... nameAndTypeWithArgs)
CallSite bootstrap(Lookup caller, String name, MethodType type)
CallSite bootstrap(Lookup caller, Object... nameAndType)
CallSite bootstrap(Lookup caller, String name, MethodType type, Object arg)
2
CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)
CallSite bootstrap(Lookup caller, String name, MethodType type, String... args)
CallSite bootstrap(Lookup caller, String name, MethodType type, String x, int y)
String
和Integer
(或int
)类型。
倒数第二个示例假定所有额外参数的类型为String
。
其他示例适用于所有类型的额外参数。
请注意,除了第二个和第三个之外的所有示例都可以使用动态计算的常量,如果返回类型更改为与常量的声明类型兼容(例如Object
,它始终兼容)。
由于动态计算的常量可以作为bootstrap方法的静态参数提供,因此对bootstrap参数的类型没有限制。 然而,类型的参数boolean
, byte
, short
,或char
不能直接通过供给CONSTANT_Integer
常量池项,由于asType
的转换没有进行必要的限制原语转换。
在上面的示例中,返回类型始终为CallSite
,但这不是引导方法的必要功能。 在动态计算的调用站点的情况下,唯一的要求是bootstrap方法的返回类型必须是可转换的(使用asType
转换)到CallSite
,这意味着引导方法返回类型可能是Object
或ConstantCallSite
。 在动态解析常量的情况下,引导方法的返回类型必须可转换为常量类型,如其字段类型描述符所表示的。 例如,如果动态常数具有的场类型描述符"C"
( char
),则自举方法的返回类型可以是Object
, Character
,或char
,但不int
或Integer
。
- 从以下版本开始:
- 1.7
-
接口摘要 接口 描述 MethodHandleInfo 通过将直接方法句柄分解为其构成符号部分而获得的符号引用。 -
类摘要 类 描述 CallSite ACallSite
是变量MethodHandle
的持有者 ,其名称为target
。ConstantBootstraps 动态计算常量的Bootstrap方法。ConstantCallSite AConstantCallSite
是CallSite
,其目标是永久性的,永远无法更改。LambdaMetafactory 通过委托给提供的MethodHandle
,可能在对参数进行类型调整和部分评估之后,便于创建实现一个或多个接口的简单“功能对象”的方法。MethodHandle 方法句柄是对基础方法,构造函数,字段或类似的低级操作的类型化,直接可执行的引用,具有可选的参数或返回值转换。MethodHandleProxies 此类仅包含静态方法,这些方法有助于将方法句柄调整为其他JVM类型,例如接口。MethodHandles 此类仅包含对方法句柄进行操作或返回方法句柄的静态方法。MethodHandles.Lookup 查找对象是用于在创建需要访问检查时创建方法句柄的工厂。MethodType 方法类型表示方法句柄接受和返回的参数和返回类型,或方法句柄调用者传递和期望的参数和返回类型。MutableCallSite AMutableCallSite
是CallSite
,其目标变量的行为类似于普通字段。SerializedLambda lambda表达式的序列化形式。StringConcatFactory 方便创建字符串连接方法的方法,可用于有效地连接已知类型的已知类型的参数,可能在类型适配和参数的部分评估之后。SwitchPoint SwitchPoint
是可以将状态转换发布到其他线程的对象。VarHandle VarHandle是对变量或参数定义的变量系列的动态强类型引用,包括静态字段,非静态字段,数组元素或堆外数据结构的组件。VolatileCallSite VolatileCallSite
是CallSite
,其目标就像一个volatile变量。 -
枚举摘要 Enum 描述 VarHandle.AccessMode 一组访问模式,用于指定如何访问由VarHandle引用的变量。 -
异常摘要 异常 描述 LambdaConversionException LambdaConversionExceptionStringConcatException 违反链接不变量时,StringConcatFactory
抛出StringConcatException。WrongMethodTypeException 抛出以指示代码已尝试通过错误的方法类型调用方法句柄。