请提供一段用一些众所周知的动态语言(例如JavaScript)编写的代码,以及使用invokedynamic在Java字节码中使用该代码的方式,并解释为什么invokedynamic的使用是向前迈进的一步。
我已经用Google搜索并阅读了很多关于不再那么新的invokedynamic指令,互联网上的每个人都同意它将有助于加速JVM上的动态语言。 Thanks to stackoverflow我设法用Sable / Jasmin运行我自己的字节码指令。
我已经理解invokedynamic对于惰性常量很有用,我也认为我理解了the OpenJDK takes advantage of invokedynamic for lambdas。
Oracle有a small example,但据我所知,在这种情况下使用invokedynamic会失败作为" adder"的示例。可以更简单,更快速,并使用以下字节码表示大致相同的效果:
aload whereeverAIs
checkcast java/lang/Integer
aload whereeverBIs
checkcast java/lang/Integer
invokestatic IntegerOps/adder(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;
因为出于某种原因,Oracle的bootstrap方法知道两个参数都是整数。他们甚至承认"的是:
[..]它假设参数[..]将是Integer对象。如果bootstrap方法的参数(在此示例中为callerClass,dynMethodName和dynMethodType)不同,则bootstrap方法需要额外的代码才能正确链接invokedynamic [..]。
嗯,是的,没有那种有趣的"额外的代码"在这里使用invokedynamic没有意义,是吗?
所以在那之后以及另外几个Javadoc和Blog条目之后,我认为我很好地掌握了如何在invokestatic / invokevirtual / invokevirtual或getfield工作时使用invokedynamic作为一个糟糕的替代品。
现在我很好奇如何将invokedynamic指令实际应用到真实世界的用例中,这样它实际上是对我们可以使用的传统"传统"调用(除了懒惰的常量,我得到了那些......)。
答案 0 :(得分:4)
实际上,如果你广泛地使用“懒惰创造”一词,那么懒惰操作是invokedynamic
的主要优点。例如,Java 8的lambda创建功能是一种延迟创建,它包含了包含最终将由invokedynamic
指令调用的代码的实际类在执行之前不存在的可能性。指令。
这可以投射到所有类型的脚本语言,以不同于Java字节码的形式提供代码(甚至可能在源代码中)。这里,代码可以在第一次调用方法之前编译,并在之后保持链接。但如果脚本语言支持重新定义方法,它甚至可能变得不相关。这使用了invokedynamic
的第二个重要特性,允许可变的CallSite
,这些可以在之后被更改,同时在没有重新定义的情况下频繁调用时支持最大性能。
此后更改invokedynamic
目标的可能性允许另一个选项,链接到第一次调用的解释执行,计算执行次数并仅在超过阈值后编译代码(并重新链接到编译后的代码)然后)。
关于基于运行时实例的动态方法调度,很明显invokedynamic
不能忽略调度算法。但是,如果您在运行时检测到某个特定的调用站点将始终调用相同的具体类型的方法,您可以将CallSite
重新链接到优化的代码,该代码将执行一个简短的检查,如果目标是预期的类型并执行然后,优化的操作分支到执行完整动态分派的通用代码,只有在该测试失败时。如果检测到快速路径检查失败了一定次数,该实现甚至可能会对这样的呼叫站点进行去优化。
这与JVM内部优化invokevirtual
和invokeinterface
的方式很接近,因为大多数这些指令都是在同一具体类型上调用的。因此,对于invokedynamic
,您可以使用相同的技术进行任意查找算法。
但是如果你想要一个完全不同的用例,你可以使用invokedynamic
来实现标准访问修饰符规则不支持的friend
语义。假设您有一个类A
和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
方法永久链接,之后以与普通调用相同的速度运行。