对象内存布局

2020/12/11 JVM

# 对象内存布局

# Java 对象内存布局详解

# 1. 概述

在 HotSpot 虚拟机中,对象在内存中存储的布局可以分为三个部分:

  1. 对象头(Header)
  2. 实例数据(Instance Data)
  3. 对齐填充(Padding)

# 2. 各部分详解

# 2.1 对象头(Header)

对象头是对象在内存中最重要的部分,它包含了对象的元数据信息,例如对象的类型、状态、锁信息等。 对象头可以分为两部分:

  1. Mark Word
  2. 类型指针

# 2.1.1 Mark Word

  • 作用: 存储对象自身的运行时数据,例如哈希码(HashCode)、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等信息。

  • 存储结构: Mark Word 的长度在 32 位虚拟机中是 4 字节,在 64 位虚拟机中是 8 字节。 其存储结构会随着对象状态的不同而发生变化。 例如,在 32 位的 HotSpot 虚拟机中,Mark Word 的结构如下:

    状态 标志位 内容
    未锁定 01 对象哈希码、对象分代年龄
    偏向锁 01 偏向线程 ID、偏向时间戳、对象分代年龄
    轻量级锁 00 指向栈中锁记录的指针
    重量级锁 10 指向 Monitor 对象的指针
    GC 标记 11
    可偏向 01 1:可偏向,0:不可偏向
  • 偏向锁 (Biased Locking): 一种锁优化技术,用于减少无竞争的锁获取操作的开销。 当一个线程第一次访问一个对象并获取锁时,会在对象头的 Mark Word 中记录下该线程的 ID。 以后该线程再次访问该对象时,无需再进行锁获取操作,直接判断 Mark Word 中是否存储着自己的线程 ID 即可。 只有当有其他线程尝试获取该锁时,才会进行锁撤销操作。

# 2.1.2 类型指针

  • 作用: 指向该对象的类元数据(Klass)的指针。 JVM 通过这个指针来确定该对象是哪个类的实例。
  • 长度: 类型指针的长度在 32 位虚拟机中是 4 字节,在 64 位虚拟机中是 8 字节。 但是,如果开启了指针压缩(UseCompressedOops),那么类型指针的长度会缩短为 4 字节。
  • 数组对象: 如果对象是一个数组,那么对象头中还会包含一个额外的字段,用于记录数组的长度。

# 2.2 实例数据(Instance Data)

  • 作用: 存储对象真正的有效信息,也就是程序代码中所定义的各种字段的值,包括父类继承下来的和自己定义的。
  • 存储顺序: 相同宽度的字段会被分配到一起,这样可以减少对齐填充的开销。 在 HotSpot 虚拟机中,字段的分配顺序如下:longs/doublesintsshorts/charsbytes/booleansoops(Ordinary Object Pointers,即对象引用)。
  • 对齐规则: 如果开启了指针压缩(UseCompressedOops),那么所有字段都会按照 8 字节对齐。 如果没有开启指针压缩,那么对象引用类型字段会按照 4 字节对齐。

# 2.3 对齐填充(Padding)

  • 作用: 起到占位符的作用。 由于 HotSpot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,因此当对象实例数据部分没有对齐时,需要通过对齐填充来补齐。
  • 长度: 对齐填充的长度不确定,取决于实例数据是否对齐。

# 3. 示例

假设有一个类定义如下:

class MyObject {
    int i;
    long l;
    boolean b;
    Object obj;
}

# 4. 对象访问方式

Java 程序需要通过栈上的 reference 数据来操作堆上的具体对象。 对象的访问方式取决于 JVM 虚拟机的实现,目前主流的访问方式有两种:

  1. 句柄访问
  2. 直接指针访问

# 4.1 句柄访问

  • 实现: 在堆中划分出一块内存来作为句柄池,reference 中存储的是对象的句柄地址。 句柄中包含了对象实例数据和类型数据各自的具体地址信息。
  • 优点: reference 中存储的是稳定的句柄地址,在对象被移动(例如垃圾回收)时,只需要改变句柄中的实例数据指针,而 reference 本身不需要修改。
  • 缺点: 需要额外的句柄池空间,并且访问对象时需要进行两次指针定位。

# 4.2 直接指针访问

  • 实现: reference 中存储的是对象的直接地址。
  • 优点: 访问对象时只需要进行一次指针定位,速度更快。
  • 缺点: 如果对象被移动(例如垃圾回收),那么需要修改 reference 本身的值。

HotSpot 虚拟机采用的是直接指针访问的方式。

# 5. 总结

理解 Java 对象的内存布局对于理解 JVM 的底层机制、进行性能调优至关重要。 掌握对象头、实例数据和对齐填充的含义,以及对象访问方式,有助于我们更好地编写高效、稳定的 Java 程序。

Last Updated: 2025/6/20 17:20:46