stringbuffer扩容机制影响性能,因每次扩容需复制已有内容;触发条件为count+新增长度>底层数组长度;扩容公式为新容量=旧容量×2+2,不足时直接设为所需大小;同步锁加剧开销;预分配合理初始容量可有效优化。

StringBuffer 的扩容机制直接影响性能,核心问题在于每次扩容都必须复制已有内容,而这个过程开销集中、不可省略。
扩容触发时机决定性能损耗频率
扩容不是按“用了多少”判断,而是看“要放多少”是否超出当前底层数组长度。只要 当前已存字符数(count) + 即将追加/插入的字符长度 > 底层数组长度(value.length),就会立即触发扩容。
- 例如初始容量16,append一个20字符字符串,一次就超限,立刻扩容
- 若分10次append两个字符,前8次不扩容;第9次时count=16,再append 2个,16+2>16 → 触发扩容
- insert操作同样适用该规则,插入位置 + 新增长度 > 当前容量也会触发
扩容公式导致数组复制成本递增
扩容不是简单翻倍,而是执行:新容量 = 当前数组长度 × 2 + 2。比如:16 → 34 → 70 → 142……
- 这个+2设计避免小容量下反复扩容,但本质仍是倍增策略
- 若计算出的新容量仍小于实际所需(如当前34,却要append 200字符),则直接设为200,跳过倍增
- 每次扩容都包含三步:分配新数组 → 用System.arraycopy复制count个字符 → 更新内部引用
- 复制量取决于当前已存字符数,不是容量本身;已有3000字符时扩容,就要拷贝3000次
同步锁放大扩容开销
StringBuffer所有方法都加synchronized,扩容时会叠加双重同步成本:
- 先同步检查容量是否足够
- 再同步执行数组分配与复制
- 多线程同时触发扩容时,不仅争抢同一把锁,还可能重复执行冗余拷贝
- 链式调用如sb.append("a").append("b")会多次进出同步块,进一步拉低吞吐
预分配容量是最直接有效的优化手段
默认构造器给16,对多数业务偏小。合理预估并设置初始容量,可大幅减少甚至消除扩容:
- 静态拼接(如日志模板):模板长度 + 各变量最大可能长度 + 10%~15%余量
- 集合批量拼接:遍历求和所有字符串length,再+32或向上取整到最近2的幂(如1800→2048)
- 循环拼接N条记录:每条平均L字节,用new StringBuffer(N * L + 64)
- 避免极端预留:设成5000却只拼200字符,浪费堆内存,反而增加GC负担
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











