Module  java.base

Package java.lang.invoke

java.lang.invoke包包含由Java核心类库和虚拟机直接提供的动态语言支持。

如Java虚拟机规范中所述,此包中的某些类型与虚拟机中的动态语言支持具有特殊关系:

相关Java虚拟机更改摘要

以下低级信息总结了Java虚拟机规范的相关部分。 有关详细信息,请参阅该规范的当前版本。 每次发生invokedynamic指令称为动态呼叫站点

invokedynamic说明

动态呼叫站点最初处于未连接状态。 在这种状态下,呼叫站点没有调用的目标方法。

在JVM可以执行动态呼叫站点( invokedynamic指令)之前,呼叫站点必须首先被链接 链接是通过调用引用方法来实现的,该方法被赋予呼叫站点的静态信息内容,并且必须产生给出呼叫站点行为的method handle

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

链接起始于解决引导方法的常量池条目,并为动态调用站点的类型描述符解析MethodType对象。 该解析过程可能会触发类加载。 如果类无法加载,它可能会抛出错误。 该错误成为动态呼叫站点执行的异常终止。 Linkage不会触发类初始化。

至少三个值调用引导方法:

  • 一个MethodHandles.Lookup ,一个在动态调用站点发生的调用者类上的查找对象
  • 一个String ,呼叫站点中提到的方法名称
  • 一个MethodType ,解析了类型描述符的调用
  • 可选地,从常量池获取1到251个额外的静态参数
调用就像MethodHandle.invoke一样 返回的结果必须是一个CallSite (或一个子类),否则抛出一个BootstrapMethodError 呼叫站点目标的类型必须与从动态调用站点的类型描述符派生的类型完全相同,并传递给引导方法,否则抛出一个BootstrapMethodError 成功后,呼叫站点将永久链接到动态呼叫站点。

如果一个异常, E说,当链接调用站点时,链接失败并异常终止。 E是重新抛出如果类型EError或子类,否则BootstrapMethodError ,它包装E被抛出。 如果发生这种情况,相同的Error或子类将被抛出,用于执行动态调用站点的所有后续尝试。

联动时机

动态呼叫站点在其第一次执行之前已链接。 引导程序调用实现链接发生在正在尝试执行第一次的线程中。

如果有多个这样的线程,引导方法可能会同时在多个线程中调用。 因此,访问全局应用程序数据的引导方法必须采取针对竞争条件的通常预防措施。 无论如何,每个invokedynamic指令都将被取消链接或链接到唯一的CallSite对象。

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

如果多个线程同时为单个动态调用站点执行引导方法,那么JVM必须选择一个CallSite对象并将其可见地安装到所有线程。 允许任何其他引导方法调用完成,但其结果将被忽略,并且其动态调用站点调用继续处理原始选择的目标对象。

讨论:这些规则不能使JVM重复动态调用站点,也不会发出“无效”引导方法调用。 每个动态调用站点在其第一次调用之前,最多只能从一个链接转换为一个链接。 没有办法撤销完成的引导方法调用的效果。

引导方式的类型

只要每个引导方法可以通过MethodHandle.invoke正确调用,其详细类型是任意的。 例如,第一个参数可以是Object而不是MethodHandles.Lookup ,返回类型也可以是Object而不是CallSite (请注意,堆栈参数的类型和数量限制了合法类型的引导方法,以适当类型的静态方法和CallSite子类的构造函数。)

如果给定的invokedynamic指令没有指定静态参数,则将在三个参数上调用指令的引导方法,传达指令的调用者类,名称和方法类型。 如果invokedynamic指令指定一个或多个静态参数,那些值将作为附加参数传递给方法句柄。 (请注意,因为任何方法都有255个参数的限制,因此可以提供最多251个额外的参数,因为引导方法处理本身,并且前三个参数也必须被堆叠。)引导方法将被调用,就像通过或者是MethodHandle.invokeinvokeWithArguments (没有办法说出差异。)

MethodHandle.invoke的常规参数转换规则适用于所有堆栈参数。 例如,如果推送的值是原始类型,则可以通过拳击转换将其转换为引用。 如果引导方法是一个变量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) 0 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) 2 CallSite bootstrap(Lookup caller, String name, MethodType type, String... args) 2 CallSite bootstrap(Lookup caller, String name, MethodType type, String x, int y)
最后一个例子假设额外的参数分别是CONSTANT_StringCONSTANT_Integer 第二个到最后一个例子假定所有额外的参数都是类型CONSTANT_String 其他示例适用于所有类型的额外参数。

如上所述,引导方法的实际方法类型可以变化。 例如,第四个参数可以是MethodHandle ,如果是CONSTANT_InvokeDynamic条目中对应的常量的类型。 在这种情况下, MethodHandle.invoke调用将通过额外的方法句柄常数作为Object ,但是在调用引导方法之前,类型匹配机制为MethodHandle.invoke将引用返回到MethodHandle (如果传递一个字符串常量,那么通过生成的代码不大,那么该转换将失败,导致一个BootstrapMethodError ))

请注意,作为上述规则的结果,引导方法可以接受原始参数,如果它可以由常量池条目表示。 然而,类型的参数booleanbyteshort ,或char不能自举方法来创建的,因为这样的常量不能直接在常量池中表示,并且自举方法的调用将不执行必要的基本收缩转换。

额外的引导方法参数旨在允许语言实现者安全和紧凑地编码元数据。 原则上,名称和额外的参数是冗余的,因为每个调用站点都可以被赋予自己独特的引导方法。 这种做法很可能会产生大型的类文件和常量池。

从以下版本开始:
1.7