对象的内存布局
1.对象头(Object Header)
第一部分存储对象自身运行时数据,如哈希码(HashCode)、GC分带年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据长度在32位和64位虚拟机(未开启指针压缩)中分别为32bit和64bit,官方称他为“Mark Word”。
第二部分类型指针,即指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个class的是例。
如果对象是个java数组,那在对象头中还必须有一块用于记录数组长度的数据。因为虚拟机可以通过普通Java对象元数据确定对象大小,但是数据的元数据中无法确定数据的大小。
2.实例数据(Instance Data)
这部分是对象真正存储的有效信息,也是程序代码中所定义的各种类型的字段内容,包含父类中继承的。这部分的存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在Java源码中定义顺序影响。
3.对齐填充(Padding)
对象对齐填充不是必然存在的,也没有特别含义,它仅仅起着占位符的作用。由于 HotSpot VM 的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,也就是说对象的大小必须是 8 字节的整数倍。对象头部分是 8 字节的倍数,所以当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。
对象的访问定位
建立对象是为了使用对象,java程序通过栈上的reference数据来操作堆上具体对象。reference访问对象分两种,由虚拟机实现而决定使用哪种。
1:使用句柄访问:java堆中将划分出一块内存来作为句柄池,reference中存储的就是对象句柄池的地址,而句柄中包含了对象的实例数据与类型数据的具体地址。这种访问方式好处就是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)只会改变句柄中实例数据的指针而reference本身不需要修改。
2:使用直接指针好处就是定位和访问对象的速度快,节省一次指针定位的开销(对象实例数据定位的节省,对类型数据的定位还有需要定位指针),java中访问对象是非常频繁的。