p49
在HptSpot虚拟机中,对象在内存中存储的布局可以分为三个区域,对象头,实例数据和对齐填充。
对象头:包含两部分信息,第一部分用于存储对象自身的运行时数据,例如哈希码,GC分代年龄,锁状态标识,线程所持有的锁等,这部分的数据的长度在32位和64位的虚拟机中分别为32位和64位,官方称其为“Mark Word”。
对象头的另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象时哪个类的实例。
另外,如果对象时一个Java数组,那么在对象头中还必须有一块用于记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据信息却无法确定数组的大小。
接下来的实例数据部分是对象真正存储的有效信息,也就是在程序代码中定义的各种类型的字段内容。从分配策略可以看出,相同宽度的字段总是被分配到一起,在满足这个前提的条件下,在父类中定义的变量会出现在子类之前。
第三部分对齐填充不是必然存在的,仅仅是占位符的作用。这是因为虚拟机要求对象起始地址必需是8字节的整数倍。
对象的访问定位
建立对象是为了使用对象。我们的Java程序通过栈上的reference数据来操作堆上的具体对象。在虚拟机规范中,reference类型中只规定了一个指向对象的引用,并没有定义这个引用使用什么方式去定位,访问堆中的对象的具体位置。目前的主流的访问方式有使用句柄和直接指针两种。
如果使用句柄的话,那么Java堆中会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。
如果使用直接指针访问,那么Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象地址。
使用句柄访问的最大好处就是,reference中存储的是稳定的句柄信息,在对象被移动时,只会改变句柄中的实例数据指针,而reference本身不需要修改。
使用直接指针访问的最大好处是,速度更快。