Java字节码编译中是如何处理字符串连接的?

问题描述 投票:0回答:1
public class TestException extends Exception {

  public TestException(String msg) {
    super("This is the message: " + msg);
  }
}

以上代码编译后为:

public class TestException extends java.lang.Exception {
  public TestException(java.lang.String);
    Code:
       0: aload_0
       1: new           #1                  // class java/lang/StringBuilder
       4: dup
       5: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
       8: ldc           #7                  // String This is the message:
      10: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      13: aload_1
      14: invokevirtual #9                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      17: invokevirtual #13                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      20: invokespecial #17                 // Method java/lang/Exception."<init>":(Ljava/lang/String;)V
      23: return
}

这里创建了一个

StringBuilder
对象来连接字符串。

在另一个地方的同一个应用程序中,完全相同的类被编译为:

public class TestException extends java.lang.Exception {
  public TestException(java.lang.String);
    Code:
       0: aload_0
       1: aload_1
       2: invokedynamic #1,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
       7: invokespecial #5                  // Method java/lang/Exception."<init>":(Ljava/lang/String;)V
      10: return
}

这里调用

StringConcatFactory.makeConcatWithConstants()
来连接字符串。

哪些因素决定了这种不同的行为?如何预测/控制?


PS:
在前一种情况下,会调用

ldc
指令,可以通过重写
MethodVisitor.visitLdcInsn()
方法来拦截该指令,并且可以对 String 进行操作。

要解决后一种行为

MethodVisitor.visitInvokeDynamicInsn()
回调也需要被重写。

我不确定是否还有其他可能的行为。

java bytecode string-concatenation java-bytecode-asm bytecode-manipulation
1个回答
0
投票

Java 9 中的字符串连接行为已更改。

在前一种情况下,该类属于使用

-source 8 -target 8
构建的主应用程序。而在后一种情况下,该类属于一个库,该库(应用程序的依赖项)是用
-source 11 -target 11
构建的。因此编译后的字节码有所不同。字符串连接。

感谢 @Stephen C@Kayaman 提供链接:


为了完整起见,正如评论中提到的@Holger,在编译的字节码中进行字符串操作时,

StringBuilder
String.concat()
String.join()
是相同的。因为在所有三种情况下,字符串常量首先使用
ldc
压入堆栈(可以简单地对其进行操作)。但在
StringConcatFactory.makeConcatWithConstants()
的情况下,首先使用
aload
将局部字符串变量的引用推入堆栈。

© www.soinside.com 2019 - 2024. All rights reserved.