什么是调用动态,我如何使用它?介绍印地 用户可定义的字节码 印地如何工作? 示例:Java 14 记录 为什么选择印地? 其他示例
我一直听说所有新的很酷的功能正在添加到JVM中,其中一个很酷的功能是调用动力学。我想知道它是什么,它如何使Java中的反思性编程更容易或更好?
我一直听说所有新的很酷的功能正在添加到JVM中,其中一个很酷的功能是调用动力学。我想知道它是什么,它如何使Java中的反思性编程更容易或更好?
作为我的Java Records文章的一部分,我阐述了调用动态背后的动机。让我们从Indy的粗略定义开始。
Invoke Dynamic(也称为Indy)是JSR 292的一部分,旨在增强对Dynamic Type Languages的JVM支持。在Java 7中首次发布后,操作码及其行李箱被基于JVM的动态语言(如JRuby)广泛使用。invokedynamic
java.lang.invoke
虽然indy专门设计用于增强动态语言支持,但它提供的远不止于此。事实上,它适用于语言设计师需要任何形式的动态性的任何地方,从动态类型杂技到动态策略!
例如,Java 8 Lambda 表达式实际上是使用 实现的,即使 Java 是一种静态类型语言!invokedynamic
很长一段时间以来,JVM 确实支持四种方法调用类型:调用静态方法、调用接口方法、调用构造函数或私有方法和调用实例方法。invokestatic
invokeinterface
invokespecial
super()
invokevirtual
尽管它们存在差异,但这些调用类型有一个共同的特征:我们不能用自己的逻辑来丰富它们。相反,使我们能够以任何我们想要的方式引导调用过程。然后,JVM 负责直接调用 Bootstrapped 方法。invokedynamic
JVM 第一次看到指令时,它会调用一个名为 Bootstrap Method 的特殊静态方法。bootstrap 方法是我们编写的一段 Java 代码,用于准备实际的待调用逻辑:invokedynamic
然后,引导方法返回 的实例。这包含对实际方法的引用,即 .java.lang.invoke.CallSite
CallSite
MethodHandle
从现在开始,每次 JVM 再次看到此指令时,它都会跳过慢速路径并直接调用底层可执行文件。JVM 将继续跳过慢速路径,除非发生任何更改。invokedynamic
Java 14提供了一个很好的紧凑语法来声明应该是哑数据持有者的类。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
在其引导方法表中:
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
因此,调用 Records 的引导方法,该方法驻留在类中。如您所见,此引导方法需要以下参数:bootstrap
java.lang.runtime.ObjectMethods
MethodHandles.Lookup
Ljava/lang/invoke/MethodHandles$Lookup
toString
equals
hashCode
toString
ConstantCallSite
CallSite
toString
TypeDescriptor
Ljava/lang/invoke/TypeDescriptor
Class<?>
Class<Range>
min;max
MethodHandle
MethodHandle
该指令将所有这些参数传递给引导方法。反过来,Bootstrap 方法返回 的实例。这是对所请求方法实现的引用,例如。invokedynamic
ConstantCallSite
ConstantCallSite
toString
与反射 API 相反,API 非常高效,因为 JVM 可以完全看穿所有调用。因此,JVM可以应用各种优化,只要我们尽可能避免慢速路径!java.lang.invoke
除了效率论证之外,由于其简单性,该方法更可靠,更不脆弱。invokedynamic
此外,为 Java 记录生成的字节码与属性数无关。因此,更少的字节码和更快的启动时间。
最后,假设新版本的Java包含一个新的,更有效的引导方法实现。使用 ,我们的应用程序可以利用此改进,而无需重新编译。通过这种方式,我们有某种前向二进制兼容性。此外,这就是我们正在谈论的动态策略!invokedynamic
除了 Java 记录之外,调用动态还用于实现以下功能:
LambdaMetafactory
StringConcatFactory