避免 ArrayList 频繁扩容引发内存抖动,核心是预设初始容量或调用 ensureCapacity 提前分配空间,减少数组复制与临时大对象生成;批量添加优先用 addAll 而非循环 add。

避免 ArrayList 频繁扩容引发的内存抖动,核心是减少数组复制次数和临时大对象的生成——因为每次扩容都要 new 一个更大的 Object[],复制旧元素,而被丢弃的旧数组若未及时回收,会在 Young GC 中堆积,造成短时内存峰值和 GC 压力上升。
预估规模,创建时直接指定初始容量
这是最简单、最有效的手段。默认构造器(new ArrayList())初始容量为 10,插入第 11 个元素就触发首次扩容(→15),之后按 1.5 倍增长(15→22→33→49…),小数据无感,但对已知规模的数据极不经济。
- 商品列表通常 800–1200 条 → 设
new ArrayList(1200) - 日志聚合单次处理约 5 万条 → 设
new ArrayList(50000) - 分页接口最多查 5 页、每页 20 条 → 设
new ArrayList(100)
运行时才确定大小?用 ensureCapacity 提前兜底
适用于流式处理、条件拼接、数据库 COUNT 后填充等场景:先获知总数,再统一预留空间。该方法只扩大容量、不改变 size,后续 add() 不会再触发 grow()。
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
int totalCount = getCountFromDB(); list.ensureCapacity(totalCount);- 它比循环中反复
add()触发多次扩容更可控,实测可降低 80% 以上复制开销 - 即使当前数组已有部分元素,
ensureCapacity仍会按需一步到位分配(不妥协于 1.5 倍策略)
批量添加优先用 addAll,别用循环 add
addAll(Collection) 内部会一次性计算最终所需容量(size + c.size()),然后调用 grow 一步扩容;而循环 add 每次都单独判断容量,极易多次触发复制。
立即学习“Java免费学习笔记(深入)”;
- 向空 list 添加 1 万个元素:
addAll(items)通常只需 1~2 次扩容 - 同量数据用
for (x : items) list.add(x)可能扩容 10+ 次 - 哪怕
items是另一个ArrayList,addAll也能复用其size()快速估算
注意容量设置的平衡点
设太小仍要扩容,设太大则浪费堆内存、可能加剧 GC 压力——但相比扩容抖动,空间成本通常可忽略。
- 常规服务建议设到预估最大值(宁大勿小),例如均值 1000 就设 1200
- 内存敏感场景(如函数计算)可设为均值的 1.2 倍,但绝不推荐沿用默认 10
- 可通过 JVM 参数(如
-XX:+PrintGCDetails)观察是否因 ArrayList 临时数组引发高频 Minor GC

















