Java对象在HotSpot JVM堆中严格按对象头→实例数据→对齐填充三部分顺序布局:对象头含Mark Word(8字节)、类指针(4或8字节)及数组长度(仅数组,4字节);实例数据按字段宽度降序排列;对齐填充确保总大小为8字节倍数。

Java 中每个 Object 实例在堆内存中并不是简单的一块连续数据,而是按固定结构组织的三段式布局:对象头、实例数据、对齐填充。其中对象头和实例数据是核心组成部分,直接决定对象的运行时行为与内存开销。
对象头:对象的元信息中枢
对象头是每个 Java 对象的起始部分,不随字段增减而变化,包含两类关键字段:-
Mark Word(8 字节):存储动态元数据,内容随对象状态切换复用同一空间。例如:
- 无锁时存哈希码和 GC 分代年龄(4 位,最大值为 15)
- 偏向锁时存偏向线程 ID 和 epoch
- 轻量级锁时存指向栈中 Lock Record 的指针
- 重量级锁时存指向 Monitor 对象的指针
-
Klass Pointer(类指针,4 或 8 字节):指向方法区中该对象所属类的元数据(Klass 结构)。JDK8+ 默认开启指针压缩(
-XX:+UseCompressedOops),此时占 4 字节;关闭后为 8 字节。
数组对象额外多出 4 字节的 length 字段,因此数组对象头为 16 字节(8 + 4 + 4),普通对象头为 12 字节(8 + 4)。
实例数据:字段值的真实落脚点
这部分紧接对象头之后,存放所有实例字段的值,包括从父类继承的字段和当前类定义的字段。- 字段不严格按源码声明顺序排列,JVM 会进行重排序优化,通常按字段宽度从大到小分组:
- long / double(8 字节)优先
- int / float(4 字节)次之
- char / short(2 字节)
- byte / boolean(1 字节)
- 父类字段一定排在子类字段之前
- 引用类型字段只存地址(4 或 8 字节),所指对象本身在堆中另存
- JVM 默认启用
-XX:+CompactFields,允许 1 字节字段(如 boolean)插入前面字段留下的空隙,减少填充浪费
对齐填充:保障 CPU 高效访问的“补白”
JVM 要求对象总大小必须是 8 字节的整数倍,这是为了适配 CPU 缓存行(cache line)和原子指令(如 CAS)的对齐要求。- 计算方式:
(8 − (对象头字节数 + 实例数据字节数) % 8) % 8 - 举例:
-
new Object():头 12B + 数据 0B = 12B → 补 4B → 总 16B -
int id; String name; boolean active;:头 12B + (4 + 4 + 1) = 21B → 补 3B → 总 24B
-
- 填充区域不存任何有效数据,纯属空间代价,但不可或缺
为什么理解这个布局很重要
- 内存占用估算更准确(避免误用 `Runtime.freeMemory()` 粗略统计) - 解释 `hashCode()` 初始值为何在锁升级后不可再获取 - 理解 synchronized 锁升级过程(依赖 Mark Word 复用机制) - 优化对象字段顺序可降低内存占用(比如把 long 放前面,boolean 放后面挤进空隙) - 影响 GC 行为(如分代年龄存于 Mark Word,4 位限制最大为 15)不复杂但容易忽略


















