深入JVM 对象的内存布局与访问定位

在经典Java 虚拟机HotSpot中,对象在堆中的布局分为3块:

  • 对象头(object header)、
  • 实例数据(Instance Data)、
  • 对齐填充深入JVM 对象的内存布局与访问定位

 

HotSpot虚拟机的对象头包括两部分

第一部分是用于存储对象自身的运行时数据,如哈希码(hashcode)、GC分代年龄、锁相关信息,这部分数据长度在32位和64位中分别是32bit 64bit 称为 Mark Word 。对象需要存储的运行时数据很多,可能超过32位 64位所能记录的限度,但对象头的信息是与对象本身定义数据无关的额外存储成本。

第二部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定对象是哪个类的实例。但并不是所有虚拟机的实现都需要在对象数据上保留类型指针,即查找对象的类元数据信息并不一定通过对象本身。另外,如果对象是数组,对象头还需要记录数组的长度,因为虚拟机无法通过数组的元数据中确定数组的大小

 

实例数据部分

是对象真正存储的自身有效信息,也就是程序中代码所定义的各种类型的字段内容,无论是从父类继承下来,还是在子类定义的内容,都需要记录下来,这部分字段数据的存储顺序由虚拟机分配策略确定。通常是父类的数据在子类之前,具体依照具体的实现策略。

对齐填充

由于虚拟机的内存管理要求地址是一定字节的整数倍,如HotSpot要求对象初始地址位8字节的整数倍,即对象的大小必须是8的整数倍,除对象头正好是8的整数倍外,实例数据不足的部分则需要填充对齐

 

——对象的访问定位

建立对象的目的是为了使用对象,Java程序需要通过栈上的reference数据来操作堆上的对象,引用如何定位堆中对象的具体位置需要由虚拟机实现,主流访问方式有 句柄访问、直接指针

 

1  句柄访问

Java堆划分出一块内存作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据的具体地址

深入JVM 对象的内存布局与访问定位

 

2 直接访问

 

使用指针直接访问,那么Java堆对象的布局需要考虑类型数据的相关信息,reference直接指向对象地址

 

深入JVM 对象的内存布局与访问定位

 

2种方式各有优势,

使用句柄访问最大优点是reference的稳定性,在对象被移动(由于垃圾回收移动对象)时只需要改变句柄池种的数据指针即可,reference本身不需要改变

使用指针直接访问的优点是性能更快,节省了一次指针定位的开销,由于对象的访问在Java中非常频繁,所以该类方式使得虚拟机执行速度更快