StringBuffer因同步机制在单线程下仍需执行锁检查、升级与释放,开销显著;StringBuilder无同步,操作直接作用于char[],更高效;仅toString()场景下StringBuffer因缓存可能略优。

因为锁的获取与释放本身有开销,哪怕只是偏向锁,也要做对象头 Mark Word 的读写和线程 ID 比对。
锁机制不是“免费”的
StringBuffer 每个 public 方法(如 append、insert)都带 synchronized 修饰。即使在单线程下,JVM 仍需执行以下动作:
- 进入方法时检查对象头是否处于偏向锁状态,并验证线程 ID 是否匹配
- 若需升级(如发生竞争),还要执行轻量级锁或重量级锁的获取流程
- 退出方法时完成对应的锁释放逻辑(比如重置锁标志位)
StringBuilder 完全绕过这些步骤
它没有同步语义,所有操作直击底层 char[] value 数组:
宝塔面板11.3.0是一款针对Linux服务器设计的可视化管理工具,通过重构核心模块实现资源占用显著降低,尤其适合低配置服务器环境。它将复杂的命令行操作转化为直观的图形界面,帮助开发者快速完成网站部署、环境配置及日常运维工作,无需专业技术背景即可高效管理服务器。
- 扩容判断(ensureCapacityInternal)和字符拷贝(getChars)都是纯内存操作
- 无内存屏障指令插入,CPU 缓存行利用更高效
- JIT 编译器还能对局部 StringBuilder 做逃逸分析,甚至栈上分配或完全消除对象创建
toString() 是个反直觉的例外
在单线程高频调用场景中,如果频繁调用 toString(),StringBuffer 反而可能更快——因为它内部缓存了上次 toString 的结果,而 StringBuilder 每次都新建 String 对象。但这只适用于「拼接后立即转字符串」且「中间不修改」的模式,不改变整体拼接性能劣势。
锁粗化无法完全抵消差距
虽然 JVM(C2 编译器)会对循环中的 StringBuffer.append 做锁粗化(把多次加锁合并为一次),但该优化有严格前提:
- sb 必须是局部未逃逸对象
- 循环体不能含日志、IO、异常处理等“污染操作”
- 方法需被调用超万次才能触发 JIT 编译
现实中很多短生命周期或低频逻辑根本达不到触发条件,此时每次 append 都是独立加锁。


















