StringBuilder扩容非简单翻倍,而是先按“旧容量×2+2”算基础值,再与“count+新增字符数”比较取较大者,最后校验不超过Integer.MAX_VALUE-8;触发条件是count+新增长度>当前capacity,扩容本质为Arrays.copyOf数组复制。

Java StringBuilder 的容量扩容不是“动态翻倍”那么简单,它底层执行的是一个有兜底、带校验的两步计算逻辑,核心公式是:新容量 = 旧容量 × 2 + 2,但这个结果只是尝试值,最终是否采用,取决于实际需要。
扩容触发条件很明确
扩容只在 append、insert 等操作导致当前字符数(count)加上新增长度后,超过内部 char 数组长度(即 capacity)时发生。也就是说,不是“每次追加都算”,而是“真不够了才扩”。
- 比如 capacity = 16,length() = 16,此时再 append(1 个字符) → 触发扩容
- 如果 length() = 10,append("abc")(3 字符)→ 总需 13 ≤ 16,不扩容
扩容计算分两步走
真正的新容量不是固定倍增,而是先算、再比、最后定:
- 第一步:算基础扩容值 —— oldCapacity × 2 + 2(等价于 (old << 1) + 2)
- 第二步:和最小所需容量(count + 新增字符数)比较
- 若基础值 ≥ 所需最小容量,就用基础值;否则直接取所需最小容量
举例:capacity=34,当前 count=34,append 50 个字符 → 最小需 84,而 34×2+2 = 70 < 84 → 最终扩容到 84,跳过倍增。
立即学习“Java免费学习笔记(深入)”;
扩容本质是一次数组复制
确定新容量后,底层调用 Arrays.copyOf(value, newCapacity),把旧数组内容完整拷贝到新分配的 char[] 中。这一步是 O(n) 时间开销,也是性能瓶颈所在。
- 复制本身不可省,但次数越少越好
- 频繁小扩(如从 16→34→70→142)比一次大扩(16→200)更耗资源,因多次复制累加
上限与安全边界不能忽略
即使按公式算出新容量,JDK 还会做两层防护:
- 不能超过 Integer.MAX_VALUE - 8(即 MAX_ARRAY_SIZE),超限抛 OutOfMemoryError
- 若计算结果 ≤ 0(极罕见,如传入非法负值或反射绕过),则强制设为 MAX_ARRAY_SIZE
这个 -8 是 JVM 对数组元数据预留的空间,属于底层内存对齐要求。


















