<p>StringBuilder底层使用可变char[]数组(Java 9+仍沿用),初始容量16,扩容公式为newCapacity = oldCapacity * 2 + 2,预估容量可减少复制开销,对象及数组均在堆内存中分配并由GC回收。</p>

StringBuilder 底层用的是可变的字符数组,不是固定不变的,它的扩容和内存分配方式直接影响拼接性能。
底层存储结构:char[] 或 byte[]?
Java 8 及之前版本中,StringBuilder 内部使用 char[] value 存储内容,初始容量默认为 16;Java 9 开始,为节省内存,String 类改用 byte[] + coder 编码标识,但 StringBuilder 仍沿用 char[](部分 JDK 实现可能优化,但公开 API 和主流行为未强制切换)。它不共享字符串常量池,所有操作都在堆内存中进行。
- 构造时未指定容量 → 初始化一个长度为 16 的 char[] 数组
- 传入字符串构造 → 容量 = 字符串长度 + 16(例如 new StringBuilder("abc") → 数组长度为 19)
- 数组本身是可变的,且未被 final 修饰,允许后续扩容和内容修改
扩容机制:如何避免频繁 copy?
当 append 等操作导致当前字符数(count)超出数组长度时,会触发扩容。扩容不是简单翻倍,而是按公式计算新容量:newCapacity = oldCapacity * 2 + 2,再与所需最小容量比较,取较大值。
- 若 newCapacity 仍不够 → 直接设为 minimumCapacity
- 扩容本质是调用 Arrays.copyOf() 创建新数组,并将旧数据复制过去
- 复制过程涉及 System.arraycopy,开销随数据量增大而上升
- 频繁扩容会导致多次内存分配和复制,影响性能
内存分配位置与生命周期
StringBuilder 对象本身在堆内存中创建,其内部 char[] 也是堆上分配的对象。它没有静态常量池参与,也不像 String 那样有 intern 机制。每次 new StringBuilder() 都是一个全新对象,引用独立,内容互不影响。
立即学习“Java免费学习笔记(深入)”;
- 栈中只存引用变量(如 StringBuilder sb),指向堆中对象
- char[] 数组属于该对象的一部分,随对象一起被 GC 回收
- 不建议长期持有大容量 StringBuilder 实例,容易造成堆内存碎片或占用过高
高效使用的实用建议
合理预估初始容量能显著减少扩容次数,尤其在已知拼接内容长度的场景下。
- 明确长度时:new StringBuilder(预计总字符数),比如拼接 1000 个短字符串,预估总长 5000 → 传入 5000
- 循环拼接前先 setLength(0) 清空,比反复 new 更轻量
- 避免在多线程中直接共享同一 StringBuilder 实例(无同步保障)
- 大量拼接且需线程安全 → 改用 StringBuffer,但注意 synchronized 带来的性能损耗


















