Java中String链式拼接运算符`+`的内部处理机制(Java 9及之后版本)
Java中String链式拼接运算符
+的内部处理机制(Java 9及之后版本) 你提的这个问题刚好戳中了Java 9对字符串拼接优化的核心点,我来一步步给你拆解内部细节,还有你关心的装箱问题~
首先先看你给出的示例代码:
import java.util.Random; public class StringConcatCompilerOptimization { private static long compute() { var random = new Random(); var l = random.nextLong(); return l; } public static void main(String[] args) { int i = 1; byte b = 120; boolean l = true; String s = "Hello" + i + compute() + b + l + "world!"; // + concatenation chain System.out.println(s); } }
对应的javap反编译输出:
Compiled from "StringConcatCompilerOptimization.java" public class StringConcatCompilerOptimization { public StringConcatCompilerOptimization(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: iconst_1 1: istore_1 2: bipush 120 4: istore_2 5: iconst_1 6: istore_3 7: iload_1 8: invokestatic #14 // Method compute:()J 11: iload_2 12: iload_3 13: invokedynamic #19, 0 // InvokeDynamic #0:makeConcatWithConstants:(IJBZ)Ljava/lang/String; 18: astore 4 20: getstatic #23 // Field java/lang/System.out:Ljava/io/PrintStream; 23: aload 4 25: invokevirtual #29 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 28: return }
Java 9前后的拼接逻辑差异
正如你所说,Java 9之前,编译器会把"Hello" + i + compute() + b + l + "world!"这类链式拼接转换成一串StringBuilder.append()调用,每次拼接都要操作StringBuilder的实例,虽然已经比直接字符串相加(每次生成新String)高效,但还是有对象创建和方法调用的开销。
而Java 9及之后,编译器会把这段代码转换成invokedynamic指令,调用makeConcatWithConstants方法,这是一种更灵活、更高效的实现。
invokedynamic调用后的内部流程
当JVM执行到这条invokedynamic指令时,会触发以下步骤:
- 引导方法调用:JVM会调用
StringConcatFactory.makeConcatWithConstants这个JDK内部的工厂方法,它的作用是生成一个专门的字符串拼接函数。 - 模板预编译:工厂方法会根据传入的常量字符串(也就是你代码里的
"Hello"和"world!")生成一个拼接模板,把变量要插入的位置标记出来,类似内部高效格式的占位符模板。 - 生成并缓存拼接实现:工厂方法会动态生成一个适配当前参数类型(
int、long、byte、boolean)的拼接实现类,这个实现会被JVM缓存起来,后续如果遇到相同模板和参数类型的拼接,会直接复用这个实现,不用重新生成。 - 参数填充与拼接:最后,JVM会把变量参数(i的值、compute()返回的long值、b的值、l的值)传入这个拼接实现,实现会直接把基本类型转成字符串,填充到模板的对应位置,最终生成完整的拼接字符串。
关于装箱的问题
你问到的装箱是不会发生的,这也是这个优化的亮点之一:
- 传统的
StringBuilder.append()对于基本类型,其实也是直接调用append(int)、append(long)这类重载方法,内部会调用Integer.toString()等静态方法转字符串,不会装箱。 - 而
makeConcatWithConstants更进一步,它直接针对传入的基本类型参数调用对应的toString静态方法(比如Integer.toString(i)、Long.toString(compute的返回值)),完全跳过了装箱步骤,直接把基本类型转成字符串,性能比之前的StringBuilder方式还要略胜一筹。
总结一下,Java 9之后的字符串拼接通过invokedynamic实现了动态生成高效拼接代码,既避免了大量的append方法调用,又省去了装箱开销,是JDK对字符串操作的一次重要性能优化。
备注:内容来源于stack exchange,提问作者nickolay
相关产品推荐
相关产品推荐

