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

2024-01-17

问: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);
     } 
 }

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

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 项调用虚拟函数。调用的方法是println()从班级java/io/PrintStream,它的输入来自类Ljava/lang/String,其返回值为Void。

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

  1. #20 指向什么?
  2. 评论有啥用#0:makeConcatWithConstants:(I)Ljava/lang/String; means?

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


简短版本:从 Java 9 开始,Java 使用 invokedynamic 来连接字符串。

让我们稍微分解一下:

Invokedynamic有两个步骤:

  • 当第一次调用该指令时,将调用引导方法。当它返回时,调用站点将链接到引导方法的结果。
  • 后续调用将直接调用目标方法句柄 https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/invoke/MethodHandle.html.

The CallSite https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/invoke/CallSite.html只是一个持有者方法句柄 https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/invoke/MethodHandle.html。取决于CallSite使用该站点的子类稍后可能会重新链接。

如果我们查看指令,我们会在末尾看到以下内容:

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

第一部分 (#0) 表示:引导方法#0。
第二部分是名称 - 它被传递给引导方法,并且可能会也可能不会在那里使用。
第三部分是结果目标的方法类型。在我们的例子中:一个方法需要int并返回一个java.lang.String.

如果我们现在看一下引导方法#0,我们会看到一个方法引用,这里是StringConcatFactory.makeConcatWithConstants(...) https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/invoke/StringConcatFactory.html#makeConcatWithConstants(java.lang.invoke.MethodHandles.Lookup,java.lang.String,java.lang.invoke.MethodType,java.lang.String,java.lang.Object...)。 我们还看到还有一个附加参数:字符串"text + String: \u0001".

现在,引导方法的工作是返回一个 MethodHandle(在 CallSite 内),在本例中它执行此字符串连接。但它如何进行字符串连接(StringBuilder、String.format、字节码旋转、链接 MethodHandles...)对于实际的类来说并不重要。它只想连接字符串。


让我们尝试手动模拟该行为。毕竟,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);
}

(虚拟机做了更多的事情,比如它记住结果并且不会再次调用引导方法,但对于我们的小例子来说这已经足够好了)。


此时,我们可以深入了解引导方法如何完成其​​工作。
事实证明:您可以将虚拟机配置为使用不同的策略。
而且它利用了它在内部的特权地位java.base访问不复制数组的 java.lang.String 的包私有构造函数 - 如果之后不修改内容,这是安全的。

默认策略是 MethodHandle 链接。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java JVM字节码表示法,注释语法。调用动态 的相关文章

随机推荐