ensureCapacity的核心目的是预分配足够大的底层数组以避免多次System.arraycopy复制。应在首次append前统算总长并调用,按预估+5%~10%余量再向上取最近2的幂(如3800→4096),适用于动态数据、分阶段构建或复用实例场景;需通过GC日志、JFR及压测验证实效。

提前调用 ensureCapacity 的核心目的,是让 StringBuilder 在首次扩容前就分配好足够大的底层数组,从而避开多次 System.arraycopy 复制——这才是性能损耗的主因。它不是“多调几次更保险”,而是一次精准预设。
什么时候该调?只在拼接开始前统算后调一次
不要在循环里判断、不要每次 append 前都查,那是重复劳动且无效。正确时机是:所有待拼内容可估算时,在 第一次 append 之前 调用:
- 集合拼接:用
list.stream().mapToInt(String::length).sum()算出原始字符总长,再加逗号、换行、括号等固定开销 - 日志模板:如
"[{}][{}]: {}"+ 用户ID(32)+ 方法名(64)+ 消息体(512),总长约 617,加 10% 余量 → 调ensureCapacity(680) - 富文本生成:先拼固定标签头尾(如
<div class="content">和</div>),再按段落平均长度 × 数量 + 样式预留空间
设多少才合适?看预估、加余量、取 2 的幂
容量值不是越接近预估越好,而是要兼顾 JVM 内存分配效率:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 预估总长后,加 5%~10% 余量(防分隔符、字段超长、编码差异)
- 结果向上取整到最近的 2 的幂:3800 → 4096,7200 → 8192,15000 → 16384
- 理由:JVM 对 2 的幂大小的数组分配更高效,内存对齐好,也避免默认扩容路径(16→34→70→142…)带来的跳跃式复制
和构造器比,什么情况下选 ensureCapacity?
构造器(new StringBuilder(int capacity))更简洁,但前提是预估非常明确。以下场景更适合先用无参构造,再调 ensureCapacity:
立即学习“Java免费学习笔记(深入)”;
- 数据来源动态:比如解析 CSV 行前,先读头部结构,再根据字段数和类型推算本行最大长度,然后统一预留
- 流程分阶段:先构建基础模板,后续根据业务规则插入变量或条件块,可在插入前集中补足容量
- 复用实例时:已有 StringBuilder 实例(如 ThreadLocal 缓存),下次任务开始前调
sb.setLength(0)清空计数,再按新任务预估调ensureCapacity
怎么确认它真起效了?别只看代码,要看运行时指标
写了 ensureCapacity 不等于性能提升,得验证:
- 开启
-XX:+PrintGCDetails,对比 Young GC 频率是否下降——频繁短数组分配和拷贝常引发 GC 尖峰 - 用 JFR 抓
jdk.ArrayCopy事件,若调用栈中仍高频出现StringBuilder.ensureCapacityInternal,说明扩容没被规避 - 压测对比:同一逻辑,跑
new StringBuilder()和new StringBuilder(4096),看耗时与 GC 次数差距


















