invokedynamic 什么时候真正有用(除了惰性常量之外)?

2024-04-06

TL;DR

请提供一段用一些众所周知的动态语言(例如 JavaScript)编写的代码,以及该代码在使用 invokedynamic 的 Java 字节码中的样子,并解释为什么使用 invokedynamic 是一个进步。

背景

我在 google 上搜索并阅读了很多关于不再那么新的 invokedynamic 指令的内容,互联网上的每个人都同意它将有助于加速 JVM 上的动态语言。感谢堆栈溢出 https://stackoverflow.com/q/23856770/327301我设法使用 Sable/Jasmin 来运行我自己的字节码指令。

我已经了解 invokedynamic 对于惰性常量很有用,而且我也认为我了解如何OpenJDK 利用 lambda 的 invokedynamic http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html.

甲骨文有一个小例子 http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html#challenges,但据我所知,在这种情况下使用 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对象。如果引导方法的参数(在本例中为 callerClass、dynMethodName 和 dynMethodType)发生变化,则引导方法需要额外的代码才能正确链接 invokedynamic [..]。

是的,如果没有那些有趣的“附加代码”,这里使用 invokedynamic 就没有意义,不是吗?

因此,在那之后以及一些进一步的 Javadoc 和博客条目之后,我认为我已经很好地掌握了如何使用 invokedynamic 作为一个糟糕的替代品,当 invokestatic/invokevirtual/invokevirtual 或 getfield 也可以工作时。

现在我很好奇如何将invokedynamic指令实际应用到现实世界的用例中,这样它实际上比我们使用“传统”调用可以做的一些改进(除了惰性常量,我得到了那些......)。


实际上,惰性操作是其主要优点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第一次调用后,之后以与普通调用相同的速度运行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

invokedynamic 什么时候真正有用(除了惰性常量之外)? 的相关文章

随机推荐