1. 概述
本文将深入探讨 HotSpot JVM 是如何存储数组长度的,以及它在内存中的具体位置。
需要明确的是,JVM 运行时数据区的内存布局 并不在 JVM 规范中强制定义,而是由具体实现者自行决定(参考 JVM 规范)。这意味着不同的 JVM 实现可能采用完全不同的对象和数组内存布局策略。
本文聚焦于一个特定实现:HotSpot JVM。后续内容中,“JVM”一词如无特别说明,均指 HotSpot JVM。
2. 依赖准备
要查看 JVM 中数组的实际内存布局,我们将使用 Java Object Layout(简称 JOL)工具。它是 OpenJDK 提供的轻量级分析工具,能精准展示对象在堆中的分布。
需要引入以下 Maven 依赖:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
3. 数组长度的存储位置
✅ 核心结论:HotSpot JVM 将数组长度直接存储在对象头(Object Header)中。
HotSpot 使用一种叫 OOP(Ordinary Object Pointer) 的数据结构来表示对象指针。对于数组,它使用一种特殊的 OOP 类型 —— arrayOop
。每个 arrayOop
的对象头包含以下三部分:
- Mark Word:用于存储哈希码、GC 分代信息、锁状态等
- Klass Word:指向类元数据(Klass)的指针
- Array Length:4 字节的数组长度字段
⚠️ 注意:长度字段固定为 4 字节,这意味着数组最大长度受限于 Integer.MAX_VALUE
(2^31 - 1),这也是为什么 Java 数组长度不能超过 int
范围的根本原因。
验证实例
我们通过 JOL 工具打印一个 int[42]
的内存布局:
int[] ints = new int[42];
System.out.println(ClassLayout.parseInstance(ints).toPrintable());
输出结果如下:
[I object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) # mark
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) # mark
8 4 (object header) 6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363) #klass
12 4 (object header) 2a 00 00 00 (00101010 00000000 00000000 00000000) (42) # array length
16 168 int [I.<elements> N/A
Instance size: 184 bytes
🔍 关键点解析:
- OFFSET 0-7:Mark Word(8 字节,64位 JVM 下通常拆成两个 4 字节)
- OFFSET 8-11:Klass Word(4 字节)
- OFFSET 12-15:✅ Array Length(4 字节),值为 42
- OFFSET 16 起:数组元素区,每个
int
占 4 字节,42 个共 168 字节 - 总大小 184 字节 = 16(对象头)+ 168(元素)
这个布局清晰地证明了:数组长度是对象头的一部分,紧跟在 klass word 之后。
小结:内存布局结构
+---------------------+
| Mark Word (8B) |
+---------------------+
| Klass Word (4B) |
+---------------------+
| Array Length (4B) | ← 长度就在这!
+---------------------+
| Element[0] (4B) |
| Element[1] (4B) |
| ... |
| Element[41] (4B) |
+---------------------+
这种设计简单粗暴但高效 —— 获取数组长度只需一次内存偏移访问,无需遍历或额外计算。
4. 总结
- ✅ HotSpot JVM 将数组长度作为对象头的一部分,存储在
arrayOop
结构中 - ✅ 长度字段占 4 字节,类型为
int
,因此最大长度为Integer.MAX_VALUE
- ✅ 使用 JOL 工具可直观验证内存布局,是分析 JVM 底层机制的利器
- ❌ 不要误以为长度是通过方法计算或存在单独字段中 —— 它是 JVM 层面的元数据
所有示例代码均可在 GitHub 获取:https://github.com/eugenp/tutorials/tree/master/core-java-modules/core-java-jvm-2