TL;DR
请提供一段用一些著名的动态语言(例如JavaScript)编写的代码,以及在Java中使用invokedynamic的代码将是什么样子,并解释为什么在这里使用invokedynamic是向前迈进了一步。
背景
我在谷歌上搜索并阅读了很多关于不再是新的发票动态指令的文章,互联网上的每个人都认为它将有助于加速JVM上的动态语言。感谢堆叠溢出我设法用Sable/Jasmin运行了我自己的字节码指令。
我已经理解了invokedynamic对于惰性常量是有用的,我也理解了OpenJDK利用了lambdas的invokedynamic是如何实现的。
Oracle有一个小例子,但据我所知,在本例中,invokedynamic的用法与“加法器”示例的目的不同,因为“加法器”的示例可以更简单、更快,并且使用以下字节码表示的效果大致相同:
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的引导方法知道这两个参数无论如何都是整数。他们甚至“承认”:
..it假设参数..。将是Integer对象。引导方法需要额外的代码才能正确链接invokedynamic。如果引导方法的参数(在本例中,callerClass、dynMethodName和dynMethodType)有所不同。
是的,如果没有那个插入的“附加代码”,这里使用invokedynamic就没有意义了,是吗?
因此,在此之后,再加上几个Javadoc和博客条目,我认为我非常了解如何使用invokedynamic作为一个糟糕的替代工具,而invokestatic/invokevirtu/vokevirtu或getfield也同样有效。
现在,我很好奇如何实际地将invokedynamic指令应用到实际世界中,这样它实际上是对“传统”调用的一些改进(除了惰性常量之外,我得到了这些.)。
发布于 2014-12-12 19:48:38
实际上,如果广义地使用“懒惰创建”一词,懒惰操作是invokedynamic的主要优势。例如,Java8的lambda创建特性是一种延迟创建,它包括包含最终由invokedynamic指令调用的代码的实际类在执行该指令之前甚至不存在的可能性。
这可以投射到所有类型的脚本语言中,以与Java字节码不同的形式交付代码(甚至在源代码中)。在这里,代码可以在第一次调用方法之前编译,然后保持链接。但是,如果脚本语言支持方法的重新定义,它甚至可能没有链接。这使用了invokedynamic的第二个重要特性,允许在不需要重新定义的情况下频繁调用时可以更改的可变CallSite,同时支持最大的性能。
这种事后更改invokedynamic目标的可能性允许另一种选择,即在第一次调用时链接到解释的执行,计数执行次数,并在超过阈值后才编译代码(然后再重新链接到编译的代码)。
对于基于运行时实例的动态方法调度,invokedynamic显然不能简化调度算法。但是,如果您在运行时检测到某个特定的调用站点总是调用相同具体类型的方法,那么您可以将CallSite重新链接到一个优化的代码中,如果目标是预期的类型并执行优化的操作,就会执行简短的检查,但是只有在测试失败时,才会分支到执行完全动态分派的通用代码。如果检测到快速路径检查失败了一定次数,那么该实现甚至可以对这样的呼叫站点进行反优化。
这与JVM内部如何优化invokevirtual和invokeinterface非常接近,因为对于这些指令,大多数指令都是在相同的具体类型上调用的。因此,对于invokedynamic,您可以对任意查找算法使用相同的技术。
但是,如果您想要一个完全不同的用例,可以使用invokedynamic实现标准访问修饰符规则不支持的friend语义。假设您有一个类A和B,它意味着具有这样一个friend关系,因为A可以调用B的private方法。然后,所有这些调用都可以编码为具有所需名称和签名的invokedynamic指令,并指向B中的public引导方法,该方法可能如下所示:
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,运行速度与之后的普通调用相同。
https://stackoverflow.com/questions/24171950
复制相似问题