空arraylist默认占约120+字节内存,因分配10元素object[]数组、对象头、字段及对齐填充;推荐复用collections.emptylist()或new arraylist(0)。

Java空ArrayList到底占多少内存?
空的 ArrayList 不是“零开销”,它默认分配一个长度为 10 的 Object[] 数组,哪怕你只调用 new ArrayList()。JVM 还要为对象头、引用字段(elementData、size)预留空间。在 64 位 JVM 上,未开启压缩指针时,这个数组本身就要 80 字节(10 × 8),加上对象头(12 字节)、对齐填充(可能 4 字节)、两个 int 字段(8 字节),总开销常达 120+ 字节。
实操建议:
- 高频创建空集合(如 DAO 返回空列表)时,优先复用
Collections.emptyList()—— 它返回的是单例不可变对象,内存恒定约 24 字节(仅对象头 + class 指针) - 若需可变空集合,用带初始容量的构造器:
new ArrayList(0),此时elementData指向共享的EMPTY_ELEMENTDATA,避免数组分配 - 别依赖 IDE 的“内存分析”插件直接看 shallow size,它常漏掉数组底层数值存储 —— 用 JOL(Java Object Layout)工具跑
GraphLayout.parseInstance(list).toPrintable()才准
-XX:+UseCompressedOops 怎么影响 HashMap 的空实例?
开启压缩指针(默认 JDK8+ 64 位启用)后,对象引用从 8 字节缩为 4 字节,但只对堆内普通对象有效;而 HashMap 的空实例(new HashMap())内部仍会初始化一个长度为 16 的 Node[] 数组。这个数组每个元素是 8 字节(Node 对象引用),即使为空,数组本身也要 16 × 4 = 64 字节(压缩后)或 16 × 8 = 128 字节(未压缩)。
关键差异在于:数组的引用字段(table)本身大小受压缩指针影响,但数组的 element type 占用由 JVM 对齐规则和指针宽度共同决定。
立即学习“Java免费学习笔记(深入)”;
实操建议:
- 确认是否真启用了压缩指针:运行
java -XX:+PrintVMOptions -version,看输出是否含-XX:+UseCompressedOops - 空
HashMap的内存主要卡在table数组分配上,不是 key/value 存储 —— 如果确定只读,用Collections.emptyMap();如果后续必 put,且预估数量小(≤ 4),显式指定初始容量:new HashMap(4),避免扩容时复制数组 - 注意 JDK 版本差异:JDK12+ 对空
HashMap做了延迟初始化(table为 null 直到首次 put),但 JDK8–11 仍会立即分配数组
为什么 LinkedHashSet 空实例比 HashSet 多占 32 字节?
因为 LinkedHashSet 底层是 LinkedHashMap,而后者比 HashMap 多两个 Entry 类型的字段:head 和 tail(双向链表头尾指针)。每个指针在开启压缩指针时占 4 字节,未开启则占 8 字节;再加上链表节点本身的结构开销(prev/next 引用),空实例的额外成本就出来了。
常见错误现象:用 LinkedHashSet 存放日志 ID 做去重,但实际顺序无关紧要,结果内存翻倍还拖慢 GC。
实操建议:
- 仅当需要迭代顺序与插入顺序一致时才用
LinkedHashSet;否则一律选HashSet - 检查反编译字节码或使用 JFR(Java Flight Recorder)抓取对象分配热点,确认
LinkedHashSet实例是否真被高频创建 - 如果必须保持顺序但数据量极小(ArrayList + 手动
contains查重 —— 小数据下线性查找比哈希表初始化更省内存
集合空容器的内存问题在什么场景最致命?
不是单个对象,而是成千上万个空集合作为 DTO 字段或缓存 value 存在时。比如 Spring Boot 接口返回 List<user></user>,但 70% 请求查不到数据,每个响应都 new 一个空 ArrayList,GC 压力和老年代碎片会快速上升。
最容易被忽略的是:泛型擦除后,JVM 无法对不同泛型参数的空集合做类型共享 —— new ArrayList<string>()</string> 和 new ArrayList<integer>()</integer> 是两个独立对象,各自分配数组,哪怕内容全空。
实操建议:
- DTO 中集合字段统一用
private List<t> items = Collections.emptyList();</t>初始化,禁止无参构造 - MyBatis 等框架映射空结果集时,配置
defaultFetchSize或用@Options(useCache = false)避免缓存空集合实例 - 监控项加一条:JVM 中
java.util.ArrayList实例数 / 总对象数 > 5%,基本说明存在空集合滥用










