使用“ invokedynamic”-到底发生了什么?

时间:2018-11-04 10:18:55

标签: java jvm jvm-bytecode invokedynamic

背景

我目前正在出于纯学术目的使用C#编写JVM(并且将来可能会构建混合的.NET和Java / Scala应用程序。)

上下文

我编写了简单的 JAVA 类:

public class test
{
    public static String hello_world(int i)
    {
        return "Hello " + i + " World!";
    }
}

并将其编译为test.class。 当我用反编译器(作为JVM的一部分编写)对它进行反编译时,会看到以下有关此方法的说明:

iload_0
invokedynamic 2
areturn

在常量池中寻找索引2的常量时,我​​看到一个InvokeDynamic-Constant条目,其中包含以下数据:

makeConcatWithConstants : (I)Ljava/lang/String;

我想这有道理(我是.NET用户而不是JAVA用户)。

使用参数hello_world执行方法1时,在执行invokedynamic 2之前,我具有以下堆栈:

----TOP---
0x00000001
--BOTTOM--

问题

我的问题是:如何使用invokedynamic
我无法解析方法makeConcatWithConstants,因为InvokeDynamic-Constant不会给我任何提示,makeConcatWithConstants可能位于何处(see documentation)。
堆栈也不包含对堆的引用,该引用指示方法makeConcatWithConstants可以与哪种实例类型关联。

我通读了the invokedynamic docs,但我听不懂(也许是因为.NET框架对我造成了很大的“损害”。)

有人可以为我指出一些有关执行这三个指令时JVM幕后情况的示例吗? (invokedynamic的被叫者会期待什么?)

我已经在JVM中实现了invokestatic ...但是我目前无法理解invokedynamic

1 个答案:

答案 0 :(得分:2)

invokedynamic的思想是;首次遇到此字节码时,请调用引导程序方法,该方法会创建一个Callsite对象,该对象链接到需要调用的实际方法。

实际上,这通常意味着您为调用动态创建实现。

如果您使用javap -v test查看程序,则会在底部看到一个BootstrapMethods属性:

BootstrapMethods:
  0: #15 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #16 Hello \u0001 World!

在此您可以看到此特定呼叫站点的引导方法位于StringConcatFactory

Method arguments是常量参数的集合。

LookupStringMethodType的主要论点分别是;具有与呼叫站点相同特权,某些名称和呼叫站点类型的查找对象。其中第一个需要由VM在运行时提供,后两个需要由invokedynamic常量池条目以名称和类型的形式提供:

#2 = InvokeDynamic      #0:#17         // #0:makeConcatWithConstants:(I)Ljava/lang/String;

因此,要实现此字节码,您必须具备一些机制来创建查找对象,然后才能调用bootstrap方法。之后,您可以在返回的Callsite对象上调用dynamicInvoker(),这将为您提供MethodHandle,然后应为该特定的调用站点进行缓存,然后(最终)调用。

如果您想了解OpenJDK中的实现方式,可以在这里找到实现:http://hg.openjdk.java.net/jdk/jdk/file/tip/src/hotspot/share/interpreter/bytecodeInterpreter.cpp#l2446

我猜这在项目的早期阶段可能太棘手,所以现在使用-XDstringConcat=inline编译程序可能会更容易,因为它使用了旧式StringBuilder串联,实施起来应该更简单。