是,JVM通过逃逸分析确认StringBuffer未逃逸后,在C2编译阶段省略monitorenter/monitorexit指令,使其单线程性能等同StringBuilder。

在单线程场景下,StringBuffer 的同步开销确实可能被 JVM 逃逸分析(Escape Analysis)配合锁消除(Lock Elision)优化掉,但这个过程不是“直接删掉 synchronized”,而是有一套严谨的判定逻辑。
逃逸分析是前提:对象必须不逃逸出当前方法或线程
JVM 首先通过逃逸分析判断一个 StringBuffer 实例是否“逃逸”:
- 如果它只在当前方法内创建、使用,且引用未被传递给其他方法、未被存储到堆中(如静态字段、其他对象字段)、未被线程间共享,则判定为“不逃逸”;
- 典型不逃逸例子:
StringBuffer sb = new StringBuffer(); sb.append("a").append("b"); return sb.toString();—— 整个生命周期局限在栈上或局部变量中; - 一旦发生逃逸(比如将
sb传给另一个方法、赋值给static字段、作为返回值暴露出去),逃逸分析失败,锁消除就不会触发。
锁消除是结果:JVM 移除无竞争的 monitorenter/monitorexit 字节码
StringBuffer 的每个 public 方法(如 append()、toString())都用 synchronized 修饰。JIT 编译器在确认该实例不逃逸后,会进一步推断:
- 该对象仅被当前线程访问;
- 所有对它的同步方法调用,本质上不存在多线程竞争;
- 因此,对应的底层 monitor 操作(
monitorenter/monitorexit)可安全移除; - 最终生成的机器码中,不会出现加锁/解锁指令,性能等同于
StringBuilder。
需要满足的运行时条件
即使代码结构适合优化,实际生效还需满足:
立即学习“Java免费学习笔记(深入)”;
- 使用 Server VM(即 HotSpot 的 -server 模式,默认开启,但需注意 JDK 9+ 后已取消 client/server 分类,逃逸分析默认启用);
- 开启逃逸分析(
-XX:+DoEscapeAnalysis,JDK 7u40+ 默认开启); - 锁消除默认开启(
-XX:+EliminateLocks,通常默认启用); - 代码需被 JIT 编译(即执行足够多次进入 C2 编译级别),解释执行阶段不触发此优化;
- 不能显式调用
sb.wait()、sb.notify()等涉及 monitor 的操作,否则锁无法消除。
如何验证是否优化成功
可通过 JVM 参数观察:
-
-XX:+PrintCompilation查看方法是否被 C2 编译; -
-XX:+UnlockDiagnosticVMOptions -XX:+PrintEscapeAnalysis输出逃逸分析决策日志(如sb: allocated in method X, not escaped); -
-XX:+PrintOptoAssembly(需 hsdis)查看汇编,确认无lock前缀或 monitor 相关指令; - 微基准测试对比
StringBuffer和StringBuilder在热循环中的吞吐量 —— 若两者性能几乎一致,很可能已锁消除。


















