JAVA面试-JVM篇 08-对象在内存中的布局包含哪些部分

概述

Java 对象在堆内存中由对象头(Header)实例数据(Instance Data)对齐填充(Padding) 三部分组成。其中对象头又细分为 Mark Word类型指针(Klass Pointer)。理解对象内存布局是分析 JVM 内存占用、锁升级机制、GC 行为的基础。

整体布局

对象内存布局

对象头 Header

实例数据 Instance Data

对齐填充 Padding

Mark Word

类型指针
Klass Pointer

三大部分详解

组成部分 必选/可选 作用
对象头 — Mark Word 必选 存储哈希码、GC 分代年龄、锁状态标志、偏向线程ID 等运行时数据
对象头 — 类型指针 必选 指向方法区中该类的元数据(开启指针压缩后 32 位)
实例数据 必选 对象实际存储的字段值,包括父类继承的字段
对齐填充 可选 填充至 8 字节的整数倍,仅当对象大小不是 8 的倍数时存在

Mark Word 结构(64位JVM)

Mark Word 是一个动态复用的数据结构,在不同锁状态下存储不同内容:

线程获取偏向锁

竞争出现, 撤销偏向

自旋超过阈值

无竞争, 直接CAS

锁释放, 无竞争

锁释放

对象新建

无锁(001)

偏向锁(101)

轻量级锁(00)

重量级锁(10)

GC 标记(11)

25位哈希码 + 4位分代年龄
+ 1位偏向锁 + 2位锁标志(01)

23位偏向线程ID + 2位EPoch
+ 4位分代年龄 + 1位偏向锁(1)
+ 2位锁标志(01)

各状态 Mark Word 位分布

锁状态 锁标志位 存储内容
无锁 01 25位对象哈希码 + 4位分代年龄 + 1位偏向锁(0)
偏向锁 01 23位偏向线程ID + 2位Epoch + 4位分代年龄 + 1位偏向锁(1)
轻量级锁 00 30位指向栈中锁记录的指针
重量级锁 10 30位指向重量级监视器(Monitor)的指针
GC 标记 11 30位GC 转发指针

字段重排序(Field Reordering)

原始字段顺序

JVM 重新排列

按字节大小分组

double / long (8B)

int / float (4B)

short / char (2B)

byte / boolean (1B)

引用类型 (4B/8B)

填充对齐
Padding to 8B

  • JVM 会按字段字节大小重新排序,将同大小的字段排列在一起,减少内存碎片
  • 父类定义的字段优先于子类字段存放
  • 子类较宽的字段可能插入到父类的对齐间隙中(仅限 64 位 JVM 且未开启压缩指针时)

内存大小计算示例

空对象(无字段)

Mark Word
8B

Klass Pointer
4B(压缩)

合计 12B → 对齐 16B

假设一个对象有 1 个 int 和 1 个 byte 字段:

组件 大小(压缩OOPs)
Mark Word 8 B
Klass Pointer 4 B
int 字段 4 B
byte 字段 1 B
填充 7 B
合计 24 B

总结

  1. 对象由对象头 + 实例数据 + 对齐填充三部分组成
  2. Mark Word 在不同锁状态下复用存储空间,是锁升级和 GC 的基础
  3. JVM 自动字段重排序优化内存布局,减少填充浪费
  4. 开启压缩指针(-XX:+UseCompressedOops) 可显著减少对象头中类型指针和引用类型字段的大小

更多推荐