深入理解Java虚拟机之对象的内存布局
对象在内存中的布局主要分为三块,对象头,实例数据和对齐填充。
- 1、其中对象头主要包含两部分的信息,一部分是对象自身的运行时数据,例如hashcode、GC年代、锁状态、线程持有的锁、偏向线程的ID、偏向时间戳等,这部分的数据在32位虚拟机和64位虚拟机上的大小分别也是32位和64位,也称作Mark Word。因为考虑到虚拟机的空间效率,所以Mark Word被设计成为一个非固定的数据结构以便在极小的空间内存储尽量多的信息,可以根据对象的状态复用自己的空间。例如32位虚拟机中,对象如果处于未被锁定的状态下,那么Mark Word的空间中 25位会用于存储hashcode、4位用于存储GC年代、2位用于存储锁标志、1位为固定的0。除了Mark Word外,另一部分数据是类型指针,即指向这个对象的类的元数据,虚拟机可以根据类型指针知道这个对象是哪个类的实例,长度也是和Mark Word一样。当然不是所有的虚拟机都实现在对象数据上保留类型指针,也就是说查找对象类的元数据不一定要经过对象本身,这个后面再说。除了Mark Word和对象类型指针外,如果这个对象是一个数组,那么对象头还会包括一个数组长度的数据,因为虚拟机只能通过类的元数据知道对象的大小,但是不能通过数组的元数据确定数组的大小,这里我的理解是就是需要用元数据的大小乘以长度,这样就可以确定数组的大小了。
- 2、实例数据部分就是对象存储的真正有用的信息,也就是对象所包含的各种字段,不管是父类继承的还是自己定义的,都需要记录下来。字段记录顺序为long/double、int(float)、short/char、byte/boolean、普通对象,也就是说先基本数据类型后对象,基本数据类型中从长到短依次排序,长度相同的字段会被排到一起。在满足这个前提下,子类的变量位于父类的变量之后,但是如果虚拟机参数CompactFields设置为true的话,那么子类较短的变量也可能会插入父类变量之间。
- 3、对齐填充,这一部分并不是必然的,也没有什么特别的含义,就是用来对齐数据的起到占位符的作用。对象的大小必须是8字节的整倍数,对象头是32位或者64位一定是8的整倍数,但是实例数据部分就不一定了,所以需要对齐填充来补位。