Jvm 如何存储InvokedDynamic的结果?

Jvm 如何存储InvokedDynamic的结果?,jvm,java-8,invokedynamic,Jvm,Java 8,Invokedynamic,Java8引入了对一级函数的支持,允许将函数分配给变量。在这种情况下,变量必须为函数类型,由函数接口定义(仅具有一个抽象方法的接口) 因此,考虑具有以下定义的接口I和类a的示例: interface I{ int foo(); } class A implements I{ public int foo(){return 7;} public static int bar(){return 11;} } 我们可以为I类型的变量分配a的一个实例,或者为a的方法bar分配方法引用。两者

Java8引入了对一级函数的支持,允许将函数分配给变量。在这种情况下,变量必须为函数类型,由函数接口定义(仅具有一个抽象方法的接口)

因此,考虑具有以下定义的接口
I
和类
a
的示例:

interface I{ int foo(); }
class A implements I{ 
  public int foo(){return 7;} 
  public static int bar(){return 11;}
}
我们可以为
I
类型的变量分配
a
的一个实例,或者为
a
的方法
bar
分配方法引用。两者都可以存储在
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
0:new#2//A类
3:dup
4:调用特殊的#3//方法A.“”:()V
7:astore_1
8:invokedynamic#4,0//invokedynamic#0:foo:()LI;
13:astore_2
对于
i1=newa()
这显然是相应的指令
7:astore_1
正在存储与
I
兼容的
A
的实例。但是,作为
i2=a::bar
的结果,我们正在存储
8:invokedynamic#4,0
的结果


因此,这意味着
invokedynamic
的结果始终是目标类型的一个实例,这是我们用方法引用分配的变量类型?

每个
invokedynamic
字节码引用常量池中的相应结构。此结构包含用于派生此
invokedynamic
指令的参数类型和返回值类型的

在您的示例中,方法描述符是
()LI在源代码到字节码转换期间计算

8: invokedynamic #4,  0              // InvokeDynamic #0:foo:()LI;
                                                             ^^^^^

这意味着这个特定字节码不需要参数,并且总是产生类型为
I

的结果
invokedynamic
指令的结果,Java 8的lambda表达式和方法引用使用它的方式,实际上是目标函数
接口的一个实例

JVM记住的不是
invokedynamic
指令的结果,而是由bootstrap方法返回的
CallSite
,如果是新的Java 8,则使用

链接到
invokedynamic
指令的
CallSite
实例封装了行为,而不是特定的结果值。
LambdaMetafactory
提供的实际行为是故意未指定的,以提供广泛的自由度,但是当前的实现展示了两种不同的行为

对于非捕获lambda表达式,行为将是返回在
invokedynamic
引导过程中创建的单个实例。这可以通过创建包装在中的。在这种情况下,
invokedynamic
指令的后续执行将计算到此实例

对于捕获值的lambda表达式,将针对接受捕获值的生成类的构造函数或工厂方法链接指令。因此,
invokedynamic
指令的后续执行行为将类似于普通对象构造(每次都会创建实现目标
接口的类的新实例)