实际上,惰性操作是其主要优点invokedynamic
如果你广义地理解“惰性创造”这个词。例如,Java 8 的 lambda 创建功能是一种惰性创建,其中包含以下可能性:包含最终由invokedynamic
该指令在执行之前甚至不存在。
这可以投射到以与 Java 字节码不同的形式提供代码的所有类型的脚本语言(甚至可能是源代码)。在这里,代码可以在第一次调用方法之前编译,并在之后保持链接。但如果脚本语言支持方法的重新定义,它甚至可能变得不链接。这使用了第二个重要特征invokedynamic
,允许可变CallSite
之后可以更改,同时在频繁调用时支持最大性能而无需重新定义。
这种可能性改变invokedynamic
之后的 target 允许另一个选项,在第一次调用时链接到解释执行,计算执行次数并仅在超过阈值后编译代码(然后重新链接到编译的代码)。
关于基于运行时实例的动态方法分派,很明显invokedynamic
无法省略调度算法。但是,如果您在运行时检测到特定的调用站点将始终调用相同具体类型的方法,您可以重新链接CallSite
到优化的代码,该代码将进行简短的检查,如果目标是预期的类型,然后执行优化的操作,但仅在测试失败时才分支到执行完整动态调度的通用代码。如果检测到快速路径检查失败一定次数,该实现甚至可以取消优化这样的调用站点。
这接近于如何invokevirtual
and invokeinterface
在 JVM 内部进行了优化,而且大多数指令都是在相同的具体类型上调用的。所以与invokedynamic
您可以对任意查找算法使用相同的技术。
但如果您想要一个完全不同的用例,您可以使用invokedynamic
实施friend
标准访问修饰符规则不支持的语义。假设你有一堂课A
and B
这意味着有这样一个friend
在那方面的关系A
被允许调用private
的方法B
。那么所有这些调用可以被编码为invokedynamic
带有所需名称和签名并指向public
中的引导方法B
可能看起来像这样:
public static CallSite bootStrap(Lookup l, String name, MethodType type)
throws NoSuchMethodException, IllegalAccessException {
if(l.lookupClass()!=A.class || (l.lookupModes()&0xf)!=0xf)
throw new SecurityException("unprivileged caller");
l=MethodHandles.lookup();
return new ConstantCallSite(l.findStatic(B.class, name, type));
}
它首先验证提供的Lookup
对象具有完全访问权限A
只作为A
能够构造这样的对象。因此,错误呼叫者的偷偷摸摸的尝试就在这个地方得到了解决。然后它使用一个Lookup
具有完全访问权限的对象B
来完成联动。所以,这些中的每一个invokedynamic
说明永久链接到匹配的private
的方法B
第一次调用后,之后以与普通调用相同的速度运行。