InvokedDynamic什么时候真正有用(除了惰性常量)? TL;博士

InvokedDynamic什么时候真正有用(除了惰性常量)? TL;博士,dynamic,jvm,invokedynamic,Dynamic,Jvm,Invokedynamic,请提供一段用一些著名的动态语言(例如JavaScript)编写的代码,以及使用InvokedDynamic在Java字节码中的代码外观,并解释为什么使用InvokedDynamic是向前迈出的一步 背景 我在谷歌上搜索并阅读了很多关于不再是新的invokedynamic指令的内容,互联网上的每个人都认为它将有助于提高JVM上的动态语言速度。我设法用Sable/Jasmin运行自己的字节码指令 我知道invokedynamic对惰性常量很有用,我也认为我了解了如何使用它 Oracle有,但据我所知

请提供一段用一些著名的动态语言(例如JavaScript)编写的代码,以及使用InvokedDynamic在Java字节码中的代码外观,并解释为什么使用InvokedDynamic是向前迈出的一步

背景 我在谷歌上搜索并阅读了很多关于不再是新的invokedynamic指令的内容,互联网上的每个人都认为它将有助于提高JVM上的动态语言速度。我设法用Sable/Jasmin运行自己的字节码指令

我知道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的引导方法知道这两个参数无论如何都是整数。他们甚至“承认”:

[…]它假定参数[…]将是整数对象。如果引导方法的参数(在本例中为callerClass、dynMethodName和dynMethodType)不同,则引导方法需要额外的代码来正确链接InvokedDynamic[…]

是的,如果没有有趣的“附加代码”,在这里使用invokedynamic是没有意义的,是吗

因此,在这之后,再加上几篇Javadoc和博客文章,我认为我已经很好地掌握了如何在invokestatic/invokevirtual/invokevirtual或getfield同样有效的情况下使用invokedynamic作为一个差劲的替代品


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

实际上,如果你使用这个术语,惰性操作是
invokedynamic
的主要优势广义的“惰性创建”。例如,Java 8的lambda创建特性是一种惰性创建,它包括这样一种可能性,即包含将由
invokedynamic
指令最终调用的代码的实际类在该指令执行之前甚至不存在

这可以投射到所有类型的脚本语言中,以不同于Java字节码的形式交付代码(甚至可能是源代码)。在这里,代码可以在第一次调用方法之前编译,并在之后保持链接。但如果脚本语言支持重新定义方法,它甚至可能会断开链接。这使用了
invokedynamic
的第二个重要功能,允许可变的
调用站点
,这些调用站点可以在在频繁调用时支持最大性能,无需重新定义

这种事后更改
invokedynamic
目标的可能性允许另一种选择,即在第一次调用时链接到解释执行,计算执行次数,并仅在超过阈值后编译代码(然后重新链接到编译后的代码)


关于基于运行时实例的动态方法调度,很明显,
invokedynamic
不能省略调度算法。但是,如果在运行时检测到某个特定调用站点总是调用相同具体类型的方法,则可以将
CallSite
重新链接到一个优化的代码,该代码将执行简短检查如果目标是预期类型并执行优化操作,则仅当测试失败时,才会分支到执行完全动态分派的通用代码。如果实现检测到快速路径检查失败了一定次数,则甚至可能会对此类调用站点进行去优化

这接近于在JVM中如何对
invokevirtual
invokeinterface
进行内部优化。对于这些指令,大多数指令也是在同一具体类型上调用的。因此,使用
invokedynamic
可以对任意查找算法使用相同的技术


但是如果你想要一个完全不同的用例,你可以使用
invokedynamic
来实现
friend
语义,这是标准访问修饰符规则不支持的我们要调用
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
能够构造这样一个对象。因此,在这个位置会对错误调用方的偷偷尝试进行分类。然后它使用对
B
具有完全访问权限的
Lookup
对象来完成链接。因此,每个t这些
invokedynamic
指令在第一次调用后永久链接到
B
的匹配
private
方法,并以与之后普通调用相同的速度运行。

关于您的最后一个代码片段,我认为您可以通过使用
StackWalker::getCallerClass
实现同样的效果>privateLookupIn
,前提是至少有java-9。@Eugene此示例是在java 9存在之前编写的。但是无论如何,
privateLookupIn
要求目标类位于同一个模块中或打开到另一个模块进行反射。相比之下,上面的代码片段允许在没有sa的情况下授予对特定工件的特定访问权限就它存在的范围而言,它仍然让人觉得很奇怪?你会