javaJVM字节码表示法,注释语法.调用动态



问题:第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/PrintStream类中的println(),其输入来自Ljava/lang/String类,其返回值为Void。

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

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

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

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

让我们把它分解一下:

Invokedynamic有两个步骤:

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

CallSite只是该MethodHandle的持有者。根据所使用的CallSite子类,该网站可能稍后会重新链接。

如果我们看一下说明书,我们会在末尾看到以下内容:

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

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

如果我们现在看一下引导方法#0,我们会看到一个方法引用,这里是StringConcatFactory.makeConcatWithConstants(…(。我们还看到还有一个额外的参数:字符串"text + String: u0001"

bootstrap方法现在的任务是返回一个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链接。

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

参考JVM规范:

首先,无符号indexbyte1和indexbyte2用于构造到当前类的运行时常量池的索引(§2.6(,…索引处的运行时常数池条目必须是对动态计算调用站点的符号引用(§5.1(

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

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

编号#0是您已过帐的BootstrapMethods属性的索引。方法名称的含义取决于引导程序方法。此外,还有类型描述符(I)Ljava/lang/String;,因此此特定调用使用int并生成String

运行时会发生什么取决于引用的引导程序方法。此调用引用了static方法StringConcatFactory.makeConcatWithConstants(...),部分方法中,字符串串联是用Java 9编译的。

该方法的文档告诉我们,invokedynamic指令中使用的方法名称无关紧要,BootstrapMethod属性的静态参数,即text + String: u0001决定了字符串格式。u0001是"普通参数"的占位符,即int参数。

相关内容

  • 没有找到相关文章

最新更新