优化ArrayList内存需避免过度预分配并使capacity匹配size,因扩容后不自动缩容;应按需创建合适容量实例、慎用复用、必要时调用trimToSize释放空间。

优化 ArrayList 内存占用、防止数组空间闲置,核心在于**避免过度预分配,同时确保容量与实际 size 匹配**。ArrayList 的 elementData 数组一旦扩容,就不会自动缩容;即使调用 clear() 或反复 remove(),底层数组仍保留原大小,造成堆内存浪费。
明确区分 capacity 和 size
size 是当前真实元素个数,capacity(即 elementData.length)是数组能容纳的最大数量,二者无 API 直接获取 capacity,但可通过反射或估算得知。常见误区是把“能放 10000 个”当成“必须占 10000 个”,尤其在复用列表、循环处理不同批次数据时:
- 若一批数据最多 5000 条,下一批仅 200 条,却始终用同一个预分配了 10000 容量的 list,后 9800 个槽位长期空闲
- clear() 后 size=0,但 capacity 不变,后续 add 仍从索引 0 开始填,旧空间未释放
按需创建新实例,而非复用大容量列表
对生命周期短、数据规模波动大的场景,优先每次新建合适容量的 ArrayList,而不是全局复用一个“万能大容量”对象:
- 读取文件每行生成一条记录:用
new ArrayList(estimatedLineCount),而非复用一个 ensureCapacity(100000) 的静态 list - HTTP 接口响应组装:根据业务逻辑预估返回条数(如分页 size),构造对应容量的 list
- 避免将 ArrayList 作为类字段长期持有大 capacity,尤其在高并发服务中易引发堆内存堆积
必要时手动收缩容量(trimToSize)
当确认列表不会再增长,且当前 size 远小于 capacity 时,可调用 trimToSize() —— 它会将 elementData 缩小到恰好等于 size 的长度,释放多余空间:
立即学习“Java免费学习笔记(深入)”;
- 适用于批量加载 → 处理 → 只读使用(如缓存构建后不再增删)的流程
- 调用时机示例:
list.addAll(data); process(list); list.trimToSize(); - 注意:trimToSize 本身触发一次数组复制(O(size)),别在频繁修改的列表上滥用
结合业务节奏控制预分配粒度
不追求“一步到位”,而按实际写入节奏分段控制:
- 如果数据是流式分批到达(如 Kafka 消费、数据库游标分页),可为每批单独 new ArrayList(batchSize),处理完即丢弃
- 若必须复用,可在每批处理前检查当前 size 与预期差异:若
list.size() + expectedAdd > list.capacity(需估算),再 ensureCapacity,而非盲目设上限 - 对不确定总量但有硬上限的场景(如配置限制最大 1000 条),直接用
new ArrayList(1000),比先 new 再 ensure 更简洁安全


















