invokedynamic 的结果是如何存储的?
How is stored the result of the invokedynamic?
Java 8 引入了对 first-class 函数的支持,它允许将函数分配给变量。在这种情况下,变量必须是函数类型,它由函数接口(只有一个抽象方法的接口)定义。
因此,考虑具有以下定义的接口 I
和 class A
的示例:
interface I{ int foo(); }
class A implements I{
public int foo(){return 7;}
public static int bar(){return 11;}
}
我们可以将 I
类型的变量分配给 A
的实例,或者将 方法引用 分配给 bar
的方法 A
。两者都可以存储在I
类型的变量上,例如:
I i1 = new A();
I i2 = A::bar;
如果我们分析之前代码编译产生的字节码,我们将得到:
0: new #2 // class A
3: dup
4: invokespecial #3 // Method A."<init>":()V
7: astore_1
8: invokedynamic #4, 0 // InvokeDynamic #0:foo:()LI;
13: astore_2
对于i1 = new A();
,显然相应的指令7: astore_1
正在存储与I
兼容的A
实例。但是,作为 i2 = A::bar
的结果,我们正在存储 8: invokedynamic #4, 0
.
的结果
因此,这意味着 invokedynamic
的结果始终是 目标类型 的实例,这是我们分配的变量类型方法参考?
每个 invokedynamic
字节码都引用一个相应的 CONSTANT_InvokeDynamic_info structure in the constant pool. This structure contains a Method Descriptor,用于派生此 invokedynamic
指令的参数类型和 return 值的类型。
在您的示例中,方法描述符是 ()LI;
在源代码到字节码转换期间计算的。
8: invokedynamic #4, 0 // InvokeDynamic #0:foo:()LI;
^^^^^
这意味着这个特定的字节码不需要任何参数并且总是产生 I
类型的结果。
invokedynamic
指令的结果,Java 8 的 lambda 表达式和方法引用使用它的方式,确实是目标函数 interface
.[=26= 的一个实例]
JVM 记住的不是 invokedynamic
指令的结果,而是 bootstrap 方法 return 编辑的 CallSite
的结果,在新 Java 8 的案例具有 LambdaMetafactory
.
的两种方法之一
链接到 invokedynamic
指令的 CallSite
实例封装了 行为 ,而不是特定的结果值。 LambdaMetafactory
提供的实际行为有意未指定以提供广泛的自由度,但当前的实现表现出两种不同的行为。
对于非捕获 lambda 表达式,行为将是 return 在 invokedynamic
bootstrapping 期间创建的单个实例。这可以通过创建 constant wrapping MethodHandle
wrapped in a ConstantCallSite
来完成。在这种情况下,invokedynamic
指令的后续执行将评估此实例。
对于捕获值的 lambda 表达式,指令将链接到接受捕获值的生成的 class 的构造函数或工厂方法。因此,invokedynamic
指令的后续执行将表现得像一个普通的对象构造(它创建一个 class 的新实例,每次都实现目标 interface
)。
Java 8 引入了对 first-class 函数的支持,它允许将函数分配给变量。在这种情况下,变量必须是函数类型,它由函数接口(只有一个抽象方法的接口)定义。
因此,考虑具有以下定义的接口 I
和 class A
的示例:
interface I{ int foo(); }
class A implements I{
public int foo(){return 7;}
public static int bar(){return 11;}
}
我们可以将 I
类型的变量分配给 A
的实例,或者将 方法引用 分配给 bar
的方法 A
。两者都可以存储在I
类型的变量上,例如:
I i1 = new A();
I i2 = A::bar;
如果我们分析之前代码编译产生的字节码,我们将得到:
0: new #2 // class A
3: dup
4: invokespecial #3 // Method A."<init>":()V
7: astore_1
8: invokedynamic #4, 0 // InvokeDynamic #0:foo:()LI;
13: astore_2
对于i1 = new A();
,显然相应的指令7: astore_1
正在存储与I
兼容的A
实例。但是,作为 i2 = A::bar
的结果,我们正在存储 8: invokedynamic #4, 0
.
因此,这意味着 invokedynamic
的结果始终是 目标类型 的实例,这是我们分配的变量类型方法参考?
每个 invokedynamic
字节码都引用一个相应的 CONSTANT_InvokeDynamic_info structure in the constant pool. This structure contains a Method Descriptor,用于派生此 invokedynamic
指令的参数类型和 return 值的类型。
在您的示例中,方法描述符是 ()LI;
在源代码到字节码转换期间计算的。
8: invokedynamic #4, 0 // InvokeDynamic #0:foo:()LI;
^^^^^
这意味着这个特定的字节码不需要任何参数并且总是产生 I
类型的结果。
invokedynamic
指令的结果,Java 8 的 lambda 表达式和方法引用使用它的方式,确实是目标函数 interface
.[=26= 的一个实例]
JVM 记住的不是 invokedynamic
指令的结果,而是 bootstrap 方法 return 编辑的 CallSite
的结果,在新 Java 8 的案例具有 LambdaMetafactory
.
链接到 invokedynamic
指令的 CallSite
实例封装了 行为 ,而不是特定的结果值。 LambdaMetafactory
提供的实际行为有意未指定以提供广泛的自由度,但当前的实现表现出两种不同的行为。
对于非捕获 lambda 表达式,行为将是 return 在 invokedynamic
bootstrapping 期间创建的单个实例。这可以通过创建 constant wrapping MethodHandle
wrapped in a ConstantCallSite
来完成。在这种情况下,invokedynamic
指令的后续执行将评估此实例。
对于捕获值的 lambda 表达式,指令将链接到接受捕获值的生成的 class 的构造函数或工厂方法。因此,invokedynamic
指令的后续执行将表现得像一个普通的对象构造(它创建一个 class 的新实例,每次都实现目标 interface
)。