Java数组对象头比普通对象多一个Array Length字段(4或8字节),用于边界检查;分配受TLAB和大对象阈值影响;基本类型数组存原始值,引用类型数组存压缩引用;对齐填充可缓解伪共享。

Java 数组对象的内存布局和普通对象类似,但多出一个关键字段——数组长度,它被明确记录在对象头中。理解这一点,是优化大数组、高频数组场景下 JVM 内存使用效率的基础。
数组对象头比普通对象多一个长度字段
普通 Java 对象头包含 Mark Word(哈希码、锁状态、GC年龄等)和 Class Pointer(类型指针)。而数组对象头在此基础上,额外增加一个 Array Length 字段,占用 4 字节(32 位 JVM)或 8 字节(64 位 JVM,未开启压缩指针时)。这个字段在对象创建时就写入,不可修改,且 JVM 通过它做边界检查(如 ArrayIndexOutOfBoundsException)。
这意味着:相同元素类型的数组,哪怕长度为 0,其对象头也比非数组对象多出这部分开销;长度越大,实例数据区越大,但对象头大小固定不变。
数组内存分配受 TLAB 和大对象阈值双重影响
数组分配同样走堆内存路径,但触发条件更敏感:
立即学习“Java免费学习笔记(深入)”;
- 小数组(如 int[10])通常优先分配在当前线程的 TLAB(Thread Local Allocation Buffer) 中,避免并发同步开销;
- 中等数组若 TLAB 空间不足,会直接在 Eden 区分配;
- 大数组(如 byte[1MB] 或 int[256K])可能直接绕过 Eden,进入老年代——前提是 JVM 启用了 -XX:PretenureSizeThreshold 参数,且数组所需连续空间超过该阈值;
- 未设置阈值时,JVM 仍可能因 Eden 空间碎片化或一次分配过大而触发 Full GC 前的晋升尝试。
基本类型数组与引用类型数组的内存差异明显
二者实例数据区结构不同,直接影响总内存占用:
- 基本类型数组(如 int[]、long[]):实例数据区直接存放原始值,无额外引用开销。例如 int[1000] 占用约 4000 字节(+对象头+对齐填充);
- 引用类型数组(如 String[]、Object[]):实例数据区只存引用(每个引用 4 字节或 8 字节),实际对象仍在堆中独立分配。String[1000] 本身可能仅占 ~4KB,但若每个 String 都是 new 出来的,总内存消耗远超前者;
- 开启 -XX:+UseCompressedOops(默认开启,64 位 JVM)后,引用从 8 字节压缩为 4 字节,可显著降低引用数组的内存 footprint。
对齐填充与缓存行对齐可主动干预
数组对象末尾的 Padding 不只是凑整数倍,还关系到 CPU 缓存性能:
- HotSpot 要求对象总大小为 8 字节对齐,但现代 CPU 缓存行通常是 64 字节(即 8 个 long);
- 若多个热点数组字段(如 RingBuffer 的 head/tail)落在同一缓存行,可能引发 伪共享(False Sharing),导致性能下降;
- 可通过 @Contended 注解(需启用 -XX:-RestrictContended)对数组类或关键字段做缓存行隔离;
- 手动填充(如在数组前后添加 long[7] 字段)也是常见实践,尤其适用于高性能队列、缓冲区等场景。


















