有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java JVM字节码表示法,注释语法。调用动力学

问题:第14行是什么意思

使用javap-v-c反汇编以下代码:

 public class test {
     static int i = 2;
     public static void main(String[] args) {
         test x = new test();
         System.out.println("text + String: " + i);
     } 
 }

在主函数中,我们得到以下内容:

14: invokedynamic #20,  0             // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
19: invokevirtual #24                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
...
BootstrapMethods:
  0: #38 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:
      #44 text + String: \u0001

例如,第19行表示从运行时常量池中的#24项调用虚拟函数。调用的方法是来自类java/io/PrintStreamprintln(),其输入来自类Ljava/lang/String,其返回值为Void

对于第14行,#0保存对BootstrapMethod的引用,并返回一个类为CallSite的对象,对吗? 然后:

  1. 20指的是什么
  2. 注释#0:makeConcatWithConstants:(I)Ljava/lang/String;是什么意思

另外,我在哪里可以找到更多关于Javap反汇编代码语法的信息?或者什么是正确的关键字?甲骨文关于the JVM instruction set的文档似乎没有明确描述该评论的含义


共 (2) 个答案

  1. # 1 楼答案

    请参阅the JVM specification

    First, the unsigned indexbyte1 and indexbyte2 are used to construct an index into the run-time constant pool of the current class (§2.6), ... The run-time constant pool entry at the index must be a symbolic reference to a dynamically-computed call site (§5.1).

    方便的是,javap已经在查找常量池并解码信息;结果是在行中的指令后面打印了一条注释

    14: invokedynamic #20,  0             // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
    

    数字#0是您已经发布的BootstrapMethods属性的索引。方法名称的含义取决于该引导方法。此外还有type descriptor{},所以这个特定的调用使用一个int并产生一个String

    运行时将发生什么取决于引用的引导方法。这个调用引用了static方法^{},其中一部分是用Java9编译字符串连接

    documentation of that method告诉我们invokedynamic指令中使用的方法名是不相关的,BootstrapMethod属性的静态参数,即text + String: \u0001确定字符串格式。\u0001是“普通参数”的占位符,即int参数

  2. # 2 楼答案

    简短版本:Java使用invokedynamic连接自Java9以来的字符串

    让我们把它分解一下:

    InvokedDynamic有两个步骤:

    • 第一次调用指令时,将调用bootstrap方法。当它返回时,调用站点将链接到引导方法的结果
    • 后续调用将直接调用目标MethodHandle

    {a2}只是该{a1}的持有者。根据所使用的CallSite子类,站点可能会在以后重新链接

    如果我们看一下说明书,最后会看到以下内容:

    #0:makeConcatWithConstants:(I)Ljava/lang/String;
    

    第一部分(#0)的意思是:Bootstrap方法#0
    第二部分是名称,它被传递给引导方法,在那里可以使用,也可以不使用
    第三部分是结果目标的方法类型。在我们的例子中:一个方法,它接受一个int并返回一个java.lang.String

    如果我们现在看一下bootstrap方法#0,我们会看到一个方法引用,这里指向StringConcatFactory.makeConcatWithConstants(...)。 我们还看到另外一个参数:字符串"text + String: \u0001"

    引导方法的任务现在是返回一个MethodHandle(在调用站点内部),在本例中,它执行字符串连接。但是它是如何进行字符串连接的(StringBuilder、string.format、字节码旋转、链接方法句柄…)对于实际的类来说并不重要。它只希望串接字符串


    让我们试着用手模仿这种行为。毕竟,bootstrap方法是一种普通的Java方法:

    public static void main(String[] args) throws Throwable {
        CallSite cs = StringConcatFactory.makeConcatWithConstants(MethodHandles.lookup(),
                "makeConcatWithConstants", MethodType.methodType(String.class, int.class),
                "text + String: \u0001");
    
        int x = 2;
        String result = (String) cs.dynamicInvoker().invokeExact(x);
        System.out.println(result);
    
        x = 3;
        result = (String) cs.dynamicInvoker().invokeExact(x);
        System.out.println(result);
    }
    

    (VM会做更多的事情,比如它会记住结果,不会再次调用bootstrap方法,但对于我们的小示例来说,这已经足够好了)


    在这一点上,我们可以深入了解引导方法是如何工作的
    事实证明:您可以将VM配置为使用不同的策略
    它使用它在java.base内的特权位置访问java的包私有构造函数。不复制数组的lang.String-如果之后不修改内容,这是安全的

    默认策略是MethodHandle链接

    好消息是:如果有人在某个时候写了一个更好的策略,您的程序将从中受益,而无需重新编译