ArrayList大数据易OOM主因是动态扩容、包装类开销及不当使用;应预设容量、用原生数组替代包装类、选fastutil等紧凑结构,并配合分批处理控内存增长。

Java 中 ArrayList 处理大数据量时容易引发内存溢出(OutOfMemoryError: Java heap space),根本原因不是 ArrayList 本身,而是它在高负载场景下叠加的三重压力:动态扩容、对象封装开销、以及不当的使用方式。解决关键在于“控住内存增长路径”,而不是单纯调大堆内存。
预设容量,切断无序扩容链
ArrayList 默认初始容量为 10,每次扩容按 1.5 倍增长(如 10 → 15 → 22 → 33…),添加 10 万条数据会触发约 17 次扩容,每次都要 Arrays.copyOf 复制全部已有元素——大量临时数组瞬间堆积在老年代,GC 来不及回收。
- 已知数据规模时,直接用带参构造:
new ArrayList(expectedSize) - 不确定但有范围(如分页查出 5k~20k 条),取上限并留 10% 余量:
new ArrayList(22000) - 若需后续扩容,优先调用
ensureCapacity(minCapacity)替代被动触发,避免中间多次小扩
避开包装类,用原生语义存数值
当 ArrayList 存的是 Integer、Double 等包装类型时,每个值都变成一个独立对象:16 字节对象头 + 4 字节字段 + 4 字节对齐填充 = 至少 24 字节/元素;而 int[] 是纯数值连续存储,仅 4 字节/元素。100 万数值用 List<Integer> 比 int[] 多占 20MB+ 内存,极易挤爆堆。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 数据库批量读取数值字段,映射为
int[]或long[],而非List<Integer> - 禁用
Arrays.asList(intArray)——它返回List<int[]>,且在泛型上下文中可能隐式装箱 - 计算聚合(求和、平均)优先走
IntStream.of(arr).sum(),不走list.stream().mapToInt(...)
慎选替代结构,规避动态数组陷阱
ArrayList 的连续内存特性在大数据量下既是优势也是负担:随机访问快,但插入/删除中间元素要移动后续所有项;扩容不可控;无法复用内存。当业务允许,可切换更紧凑或更可控的结构:
立即学习“Java免费学习笔记(深入)”;
- 纯数值密集场景(如实时指标、传感器数据),引入
fastutil的IntArrayList:内存占用比ArrayList<Integer>低约 60%,支持int直接操作 - 需要键值映射且 key 是 int/long,用
Int2ObjectOpenHashMap,避免Integer作为 key 的装箱与哈希开销 - 只做尾部追加 + 批量遍历,且数据一次性加载,考虑用普通数组
Data[]配合长度变量管理,彻底绕过 ArrayList 封装
配合分批与流控,不让单次压垮 JVM
即使 ArrayList 本身优化到位,若一次性把几百万条记录全读进内存再塞进 list,仍会 OOM。必须从数据源头控制吞吐节奏:
- 数据库查询启用分页(
LIMIT/OFFSET或游标),每次只加载 500–2000 条,处理完立即丢弃引用 - 多线程处理时,每个线程分配独立的预设容量 ArrayList,禁止共享同一 list 实例
- 导出文件(Excel/JSON)不先把全部数据转成
List<Map<String, Object>>,改用流式写入:逐行从原数组或 ResultSet 取值,直接写入输出流

















