对象内存布局:
对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据和对齐填充。
一、对象头包括两部分信息:
第一部分用户存储对象自身的运行时数据、如哈希码、GC分代年龄、线程持有的锁、偏向线程ID,偏向时间戳等,官方称它为”Mark Word“
第二部分用于存放类型指针,虚拟机可以通过这个指针来确定这个对象是哪个类的实例。另外,对象如果是一个java数组,那再对象头中还必须有一块用于记录数组长度的数据。因为虚拟机可以通过普通java对象的元数据确定java对象的大小,但是从数组的元数据中却无法确定数组的大小
二、实例数据:
实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的名称类型的字段内容。无论是从父类继承下来的,还是从子类中定义的,都需要记录下来。这部分的存储顺序会受到虚拟机分配参数和字段在java源码中定义顺序的影响。
三、对齐填充:
对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。虚拟机的自动内存管理系统要求对象起始地址必须是8个字节的整数倍,因此党实例数据部分没有对齐时,就需要通过对齐填充来补全。
备注:对象头正好是8字节的倍数,所以不会影响是否对齐填充。
对象的访问定位:
java程序需要通过栈上的reference(引用)数据来操作堆上的具体对象。对象的访问方式是取决于虚拟机实现决定的,目前主要的访问方式有使用句柄和直接指针两种。
1)句柄访问:
句柄访问最大的好处是reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针。
2)直接访问:
使用直接指针访问方式最大的好处就是速度更快,它节省了一次指针定位的开销,由于对象的访问在java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。