模块  java.base

Package java.lang.invoke

java.lang.invoke软件包提供了与Java虚拟机交互的低级基元。

如Java虚拟机规范中所述,此程序包中的某些类型由虚拟机进行特殊处理:

  • MethodHandle VarHandle包含signature polymorphic methods ,无论其类型描述符如何,都可以链接。 通常,方法链接需要类型描述符的精确匹配。
  • JVM字节码格式支持类MethodHandleMethodType立即常量。
  • invokedynamic指令使用bootstrap MethodHandle常量来动态解析CallSite对象以获取自定义方法调用行为。
  • ldc指令使用bootstrap MethodHandle常量来动态解析自定义常量值。

呼叫站点和常量的动态分辨率

以下低级信息总结了Java虚拟机规范的相关部分。 有关完整详细信息,请参阅该规范的当前版本。

动态计算的呼叫站点

invokedynamic指令最初处于未链接状态。 在这种状态下,没有指令调用的目标方法。

在JVM可以执行invokedynamic指令之前,必须首先链接指令。 链接是通过调用一个bootstrap方法来完成的,该方法被赋予调用的静态信息内容,并且必须产生一个给出调用行为的CallSite

每个invokedynamic指令将其自己的引导方法静态指定为常量池引用。 常量池引用还指定了调用的名称和方法类型描述符,就像invokestatic和其他调用指令一样。

动态计算常量

常量池可能包含标记为CONSTANT_Dynamic常量,配有执行其分辨率的引导方法。 这种动态常数最初处于未解决状态。 在JVM可以使用动态计算常量之前,必须先解决它 动态计算的恒定分辨率是通过调用一个bootstrap方法来完成的,该方法给出了常量的静态信息内容,并且必须产生常量静态声明类型的值。

每个动态计算的常量静态地将其自己的引导方法指定为常量池引用。 常量池引用还指定常量的名称和字段类型描述符,就像getstatic和其他字段引用指令一样。 (粗略地说,动态计算的常量是动态计算的呼叫站点,因为CONSTANT_FieldrefCONSTANT_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是重新抛出如果类型EError或子类,否则BootstrapMethodError ,它包装E被抛出。 如果发生这种情况,将在执行invokedynamic指令或加载动态计算常量的所有后续尝试中抛出相同的错误。

解决时间

invokedynamic指令在第一次执行之前链接。 动态计算常量在第一次使用之前解析(通过将其推入堆栈或将其作为引导方法参数链接)。 实现链接的引导方法调用发生在尝试首次执行或首次使用的线程中。

如果存在多个这样的线程,则可以在多个线程中同时调用引导方法。 因此,访问全局应用程序数据的引导方法必须采取通常的预防措施来防止竞争条件。 在任何情况下,每个invokedynamic指令都是取消链接或链接到唯一的CallSite对象。

在需要具有单独可变行为的invokedynamic指令的应用程序中,它们的引导方法应该产生不同的CallSite对象,每个链接请求一个。 或者,应用程序可以将单个CallSite对象链接到多个invokedynamic指令,在这种情况下,对每个指令的目标方法的更改都将变为可见。

如果多个线程同时为单个动态计算的调用站点或常量执行引导方法,则JVM必须选择一个引导方法结果并将其明显安装到所有线程。 允许完成任何其他引导方法调用,但忽略其结果。

讨论:这些规则不允许JVM共享呼叫站点,也不会发出“无故障”引导方法调用。 每个invokedynamic指令在第一次调用之前最多从未链接到链接转换一次。 无法撤消已完成的引导程序方法调用的效果。

引导方法的类型

对于动态计算的调用点,自举方法被调用以参数类型MethodHandles.LookupStringMethodType ,和类型中的任何静态参数; 返回类型是CallSite

对于一个动态计算的常数,所述自举方法被调用以参数类型MethodHandles.LookupString ,和类型中的任何静态参数; 返回类型是表示的类型。

因为MethodHandle.invoke允许在调用的方法类型和引导方法句柄的方法类型之间进行调整,所以引用方法的声明具有灵活性。 对于动态计算的常量,引导方法句柄的第一个参数类型必须可分配给MethodHandles.Lookup ,除了该约束之外,相同程度的灵活性适用于动态计算的调用站点和动态计算常量的引导方法。 注意:此约束允许将来仅使用静态参数的参数类型调用bootstrap方法的可能性,从而支持与静态参数兼容的更广泛的方法(例如不声明或需要查找的方法,名称和类型元数据参数)。

例如,对于动态计算的调用站点,第一个参数可以是Object而不是MethodHandles.Lookup ,返回类型也可以是Object而不是CallSite (请注意,堆叠参数的类型和数量将合法类型的引导方法限制为适当键入的静态方法和构造函数。)

如果推送值是基本类型,则可以通过装箱转换将其转换为引用。 如果引导方法是变量arity方法(其修改0x00800x0080已设置),则此处指定的一些或所有参数可能被收集到尾随数组参数中。 (这不是一个特殊规则,而是CONSTANT_MethodHandle常量之间相互作用的有用结果,变量arity方法的修饰符位和asVarargsCollector转换。)

鉴于这些规则,以下是动态计算的调用站点的合法引导方法声明的示例,给出了额外参数的各种数字N 第一行(标记为* )适用于任意数量的额外参数。

Static argument types N Sample bootstrap method *
  • CallSite bootstrap(Lookup caller, String name, MethodType type, Object... args)
  • CallSite bootstrap(Object... args)
  • CallSite bootstrap(Object caller, Object... nameAndTypeWithArgs)
0
  • CallSite bootstrap(Lookup caller, String name, MethodType type)
  • CallSite bootstrap(Lookup caller, Object... nameAndType)
1 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)
最后一个示例假定额外参数分别为StringInteger (或int )类型。 倒数第二个示例假定所有额外参数的类型为String 其他示例适用于所有类型的额外参数。 请注意,除了第二个和第三个之外的所有示例都可以使用动态计算的常量,如果返回类型更改为与常量的声明类型兼容(例如Object ,它始终兼容)。

由于动态计算的常量可以作为bootstrap方法的静态参数提供,因此对bootstrap参数的类型没有限制。 然而,类型的参数booleanbyteshort ,或char不能直接通过供给CONSTANT_Integer常量池项,由于asType的转换没有进行必要的限制原语转换。

在上面的示例中,返回类型始终为CallSite ,但这不是引导方法的必要功能。 在动态计算的调用站点的情况下,唯一的要求是bootstrap方法的返回类型必须是可转换的(使用asType转换)到CallSite ,这意味着引导方法返回类型可能是ObjectConstantCallSite 在动态解析常量的情况下,引导方法的返回类型必须可转换为常量类型,如其字段类型描述符所表示的。 例如,如果动态常数具有的场类型描述符"C"char ),则自举方法的返回类型可以是ObjectCharacter ,或char ,但不intInteger

从以下版本开始:
1.7