-xx:+optimizestringconcat 在 jdk 9+ 中已被移除,字符串拼接改由 stringconcatfactory 和 invokedynamic 动态生成字节码(如 makeconcatwithconstants),不再依赖 stringbuilder 重写;jdk 8 及以前才有效,默认开启。

为什么 -XX:+OptimizeStringConcat 在 JDK 9+ 中已失效
这个 JVM 参数在 JDK 9 及以后版本中被彻底移除,不是“不生效”,而是根本不存在。JDK 9 引入 StringConcatFactory 和 invokedynamic 后,字符串拼接优化不再依赖旧式编译器重写 + 号为 StringBuilder,而是由运行时动态生成高效字节码。所以你在 JDK 11/17/21 上加 -XX:+OptimizeStringConcat 不会报错(会被忽略),但也不会触发任何行为。
它只对 JDK 8 及更早版本有效,且默认开启;如果你在 JDK 8 环境下禁用它(-XX:-OptimizeStringConcat),就会退化为最原始的 String.concat() 链式调用,性能暴跌。
怎么验证当前 JDK 实际使用的拼接策略
直接看字节码是最可靠的方式。在 IntelliJ IDEA 中,打开任意含 + 拼接的 Java 文件,编译后执行 View → Show Bytecode(快捷键 Ctrl+B):
- 若看到
INVOKEDYNAMIC makeConcatWithConstants,说明走的是 JDK 9+ 的新路径 - 若看到
new StringBuilder、append、toString,说明是 JDK 8 或禁用了优化 - 注意:必须先编译或运行过该类,否则字节码可能未生成或不准确
makeConcatWithConstants 底层到底做了什么
StringConcatFactory.makeConcatWithConstants 不是简单地“换了个 builder”,而是根据拼接结构做策略选择:
- 所有操作数都是编译期常量(如
"a" + "b" + 42)→ 直接内联为一个常量字符串,零运行时开销 - 含变量但长度可预估(如
prefix + name + suffix)→ 默认使用MH_INLINE_SIZED_EXACT:直接分配字节数组,逐段拷贝,绕过StringBuilder的扩容和对象创建 - 含大量不确定长度的变量(如循环内拼接)→ 仍会 fallback 到
BC_SB(即传统StringBuilder方式),但这是极少数情况
它不修改源码,也不改变语义,只是把“拼接动作”从解释执行变成 JIT 编译后的一段高度定制的本地拷贝逻辑。
别被“自动优化”骗了:哪些场景它依然慢
即使 makeConcatWithConstants 开启,以下写法仍会产生严重性能问题:
- 在
for循环里写result += item:每次迭代都触发一次完整拼接流程,等价于反复 new + copy,中间字符串全成 GC 垃圾 - 拼接对象未重写
toString(),或返回 null:触发空指针或意外字符串(如"null"),且无法被常量折叠 - 混合使用
String.format、MessageFormat和+:它们不共享优化机制,format仍走解析模板路径,开销更大
真正关键的分水岭不是“用不用 +”,而是“是否在可预测上下文中一次性拼接”。循环内拼接永远不该依赖 +,哪怕在 JDK 21 也一样。










