← 返回题目列表

Java 对象在内存中的布局是怎样的?

高频 中等 第 5 / 34 题 更新于 2026/07/25
JVM对象内存布局对象头Mark Word

简化版

一个对象在堆里由三部分组成:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。对象头存「运行时元数据」——Mark Word(哈希码、GC 分代年龄、锁标志)和类型指针(指向类元信息);实例数据是各字段的值;对齐填充是补位,保证对象大小是 8 字节的整数倍

详细版

① 对象头(Header),分两部分:

  • Mark Word(64 位系统占 8 字节):存运行时数据,且内容随锁状态复用——无锁时存哈希码 + GC 分代年龄 + 锁标志位;加锁后存指向锁记录/Monitor 的指针或线程 ID。这是 synchronized 锁升级的关键载体。
  • 类型指针(Klass Pointer):指向方法区里该对象的类元信息,JVM 靠它知道「这是哪个类的对象」。开启指针压缩(默认)时占 4 字节。
  • (数组还多一个 数组长度 字段。)

② 实例数据(Instance Data):对象真正的「内容」——所有字段的值(包括从父类继承的)。JVM 会按一定规则重排字段顺序以节省空间。

③ 对齐填充(Padding):不是必需的,只是用来凑整。HotSpot 要求对象起始地址是 8 字节的整数倍,不够就补 0。

所以一个 new Object() 在 64 位开启压缩指针时是 16 字节(8 Mark Word + 4 类型指针 + 4 填充)。

完整版教学

一、为什么要有对象头——对象的「身份证 + 状态栏」

实例数据是对象的「内容」,但 JVM 管理对象还需要一堆元数据:这对象是哪个类的?它的哈希码是多少?被锁了没?经历过几次 GC?这些信息不属于业务字段,就存在对象头里。

  • 类型指针回答「你是谁」(哪个类);
  • Mark Word 回答「你的运行时状态」(锁、哈希、GC 年龄)。

可以把对象头理解成每个对象自带的一小段「身份证 + 状态栏」,JVM 靠它来做类型识别、加锁、GC、哈希等底层操作。

二、Mark Word:按状态复用的元数据

Mark Word 会按对象状态复用有限位宽,常见 HotSpot 实现用它保存哈希、GC 年龄和锁相关状态。精确位图不是 JVM 规范,且同步实现已随 JDK 演进:偏向锁是旧版 HotSpot 优化:JDK 15 默认禁用并弃用相关选项,JDK 18 将其置为 obsolete 并移除相应实现代码。

对象状态Mark Word 可能承载的信息边界
普通状态哈希、年龄、状态位等哈希可能尚未计算
轻量级同步状态与锁记录或锁状态相关的数据具体编码依 JDK 实现
重量级同步状态与 ObjectMonitor 相关的数据具体指针/索引形式可变化
GC 处理状态收集器所需的标记信息取决于收集器与实现

因此可以用“Mark Word 是对象运行时状态栏”解释设计,但不应把旧版本的固定二进制标志或单向“偏向→轻量→重量”流程当成所有现代 JDK 的永久结论。

三、一道经典追问:一个 Object 占多少字节

在常见 64 位 HotSpot、启用压缩类指针且按 8 字节对象对齐的示例配置下:

  • new Object():若 Mark Word 为 8 字节、压缩类指针为 4 字节,则头部 12 字节,再填充到 8 的倍数,示例结果为 16 字节
  • 带字段的对象:16 字节起 + 字段大小,再对齐到 8 的倍数。

指针压缩是 HotSpot 的重要优化:在可编码的堆布局下,对象引用可用较小值表示,从而降低引用和缓存占用。可压缩的堆上限受对象对齐、零基址映射和 JVM 决策影响,常见 32 GB 只是经验边界,不能据此武断限制堆大小。

四、字段重排与对齐

JVM 不一定按源码声明顺序摆放字段,而会在语义约束内采用实现相关的布局策略,以减少空隙并满足各字段的对齐要求。父类字段、字段分组以及 JVM 版本都会影响最后顺序,所以不能只按 longintboolean 的声明大小直接相加。对齐有利于地址计算和处理器访问,却也可能让小字段后的空洞变成额外开销。应在目标 JDK 与参数下使用 JOL(Java Object Layout) 查看真实偏移,而不是把某个示例布局当成规范。

五、用数字算一遍,但要声明前提

在常见 64 位 HotSpot、启用压缩类指针、对象按 8 字节对齐的某种配置下,可做如下示例:

class Sample {
    int id;       // 4 字节
    boolean ok;   // 1 字节
    Object ref;   // 压缩普通对象指针时常见 4 字节
}
组成示例字节数说明
Mark Word8具体位含义随 JVM/JDK 和锁状态变化
Klass Pointer4启用压缩类指针时的常见值
实例字段9字段可能被重排并插入空隙
对齐填充视布局而定使总大小满足对象对齐要求

简单相加是 21 字节,但实际布局可能重排为 24 字节或其他结果。必须用目标 JVM 的 JOL 输出确认,不能只凭声明顺序手算。

new Object() 常见为 16 字节,也只是特定 HotSpot 配置下的经验值;关闭压缩类指针、改变对象对齐或使用不同 JVM 都可能改变结果。

六、对象头会随运行时状态变化

Mark Word 是复用字段,可能编码哈希、GC 年龄、锁相关状态等信息。锁实现和对象头设计随 JDK 演进,不能把某个版本“偏向锁、线程 ID、锁指针”的位图当成永久标准。

压缩普通对象指针能否启用与最大堆、对象对齐、零基址映射等有关。“堆超过 32 GB 压缩指针必然关闭”只是某些典型配置下的粗略经验,阈值可能因对齐和 JVM 决策变化。应查看启动日志或实际 VM flags。

对象布局属于 JVM 实现细节。面试可以讲 HotSpot 常见结构,但要明确位数、压缩指针、对象对齐和 JDK 版本四个前提。

七、常见误区与追问

  • 误区:Java 对象只占字段声明的大小。 对象还包含对象头、父类字段、字段间空隙和末尾对齐填充。
  • 误区:所有 64 位 JVM 中 new Object() 都固定为 16 字节。 这依赖 HotSpot 配置、压缩类指针和对齐参数。
  • 误区:Mark Word 永久保存同一组固定字段。 它会按对象状态复用,其布局也随 JVM 版本变化。
  • 追问:为什么需要对象对齐? 对齐可简化地址计算和内存访问,也为压缩指针编码提供条件,但会引入填充浪费。
  • 追问:字段声明顺序等于内存顺序吗? 不一定;JVM 可能在满足继承和语义约束的前提下重排字段以减少空隙。
  • 追问:数组对象比普通对象多什么? 对象头还需要保存数组长度,元素区之后同样可能有对齐填充。
  • 追问:怎样得到线上 JVM 的真实布局? 在相同 JDK、参数和类定义下使用 JOL,并核对压缩指针与对象对齐配置。

八、加强记忆

用“头、数据、填充”记住 HotSpot 常见对象结构:对象头提供类型与运行时状态,实例数据保存字段,末尾填充满足对齐。Mark Word 会按状态复用,精确位图、偏向锁流程和锁编码都受 JDK 版本影响,不能当成 JVM 规范。new Object() 常见 16 字节以及压缩指针约 32 GB 的经验边界都必须附带位数、压缩类指针、对象对齐和堆布局前提。遇到具体大小题,先列前提再手算,最后用目标环境的 JOL 和 VM flags 验证。