我不断听到关于JVM中添加的所有新的很酷的特性,其中一个很酷的特性是invokedynamic。我想知道它是什么,它如何使Java中的反射编程变得更容易或更好?
发布于 2011-07-10 10:23:10
它是一个新的JVM指令,它允许编译器生成代码,使用比以前更宽松的规范调用方法--如果您知道"duck typing“是什么,invokedynamic基本上允许鸭子类型。作为Java程序员,您不能使用它做太多事情;但是,如果您是工具创建者,则可以使用它来构建更灵活、更高效的基于JVM的语言。Here是一篇非常贴心的博客文章,提供了很多细节。
发布于 2020-04-05 22:14:37
作为我的Java Records文章的一部分,我阐述了Invoke Dynamic背后的动机。让我们从Indy的大致定义开始。
Indy简介
Invoke Dynamic (也称为Indy)是的一部分,旨在增强JVM虚拟机对动态类型语言的支持。在Java7中首次发布之后,基于invokedynamic的动态语言(如JRuby )广泛使用了java.lang.invoke操作码和它的Java工具箱。
尽管indy专门为增强动态语言支持而设计,但它提供的远不止这些。事实上,它适用于语言设计者需要任何形式的动态性的任何地方,从动态类型杂技到动态策略!
例如,Java表达式实际上是使用invokedynamic实现的,尽管Java 8是一种静态类型的语言!
用户可定义的字节码
在相当长的一段时间内,JVM确实支持四种方法调用类型:调用静态方法的invokestatic,调用接口方法的invokeinterface,调用构造函数的invokespecial,调用实例方法的super()或私有方法和invokevirtual。
尽管它们不同,但这些调用类型有一个共同的特点:我们不能用我们自己的逻辑来丰富它们。相反,invokedynamic 使我们能够以任何我们想要的方式引导调用过程。然后,JVM负责直接调用引导方法。
Indy是如何工作的?
第一次看到invokedynamic指令时,它会调用一个名为Bootstrap Method的特殊静态方法。bootstrap方法是我们编写的一段Java代码,用于准备实际要调用的逻辑:

然后,bootstrap方法返回java.lang.invoke.CallSite的一个实例。此CallSite包含对实际方法的引用,即MethodHandle。
从现在开始,每当JVM再次看到这条invokedynamic指令时,它都会跳过慢速路径,直接调用底层的可执行文件。除非发生变化,否则JVM将继续跳过慢速路径。
示例: Java 14记录
Java14 Records提供了一个很好的紧凑语法来声明那些被认为是哑巴数据持有者的类。
考虑一下这个简单的记录:
public record Range(int min, int max) {}此示例的字节码如下所示:
Compiled from "Range.java"
public java.lang.String toString();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #18, 0 // InvokeDynamic #0:toString:(LRange;)Ljava/lang/String;
6: areturn在其Bootstrap方法表中
BootstrapMethods:
0: #41 REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:
(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;
Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;
Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
Method arguments:
#8 Range
#48 min;max
#50 REF_getField Range.min:I
#51 REF_getField Range.max:I因此,记录的bootstrap方法称为bootstrap,它驻留在java.lang.runtime.ObjectMethods类中。如您所见,此bootstrap方法需要以下参数:
Ljava/lang/invoke/MethodHandles$Lookup部件)的MethodHandles.Lookup实例。toString、equals、hashCode等)bootstrap将链接。例如,当值为toString时,bootstrap将返回一个ConstantCallSite (永不更改的CallSite ),它指向此特定记录的实际toString实现。TypeDescriptor (Ljava/lang/invoke/TypeDescriptor part)。Class<?>。分号分隔的所有组件名称的列表,例如,每个组件的min;max.MethodHandle。这样,bootstrap方法就可以基于此特定方法implementation.的组件创建MethodHandle
invokedynamic指令将所有这些参数传递给bootstrap方法。Bootstrap方法返回ConstantCallSite的一个实例。此ConstantCallSite包含对请求的方法实现的引用,例如toString。
为什么是印地?
与反射API相反,JVM非常高效,因为java.lang.invoke可以完全看透所有调用。因此,JVM可以应用各种优化,只要我们尽可能避免慢路径!
除了效率方面的争论之外,由于its simplicity,invokedynamic方法更加可靠且不那么脆弱。
此外,为Java记录生成的字节码与属性的数量无关。因此,字节码更少,启动时间更快。
最后,让我们假设新版本的Java包含一个新的、更有效的bootstrap方法实现。有了invokedynamic,我们的应用程序就可以利用这种改进,而无需重新编译。这样,我们就有了某种向前的二进制兼容性。另外,这就是我们所说的动态策略!
其他示例
除了Java记录之外,invoke dynamic还用于实现如下功能:
发布于 2011-07-10 12:59:33
不久前,C#在C#中添加了一个很酷的特性,即动态语法
Object obj = ...; // no static type available
dynamic duck = obj;
duck.quack(); // or any method. no compiler checking.可以把它看作是反射方法调用的语法糖。它可以有非常有趣的应用。请参阅http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
Neal Gafter负责C#的动态类型,他刚刚从SUN跳槽到了MS.,所以认为SUN内部也讨论过同样的事情并不是不合理的。
我记得在那之后不久,一些Java家伙宣布了类似的事情
InvokeDynamic duck = obj;
duck.quack(); 不幸的是,在Java7中找不到这个特性,非常失望。对于Java程序员来说,他们没有在程序中利用invokedynamic的简单方法。
https://stackoverflow.com/questions/6638735
复制相似问题