46.对象的内存布局(对象头、实例数据、对齐填充)和对象的访问定位(句柄访问和直接指针)
1.对象的内存布局
1.整体上来看,对象在内存中有3
部分,对象头、实例数据、对齐填充。a)
对象头包含运行时元数据和类型指针。运行时元数据包含哈希值(这个哈希值就是创建出来的对象在内存中的地址)、GC
分代年龄、锁状态标志等。类型指针,指向方法区(元空间)中对象所属的类型。如果创建的是数组,对象头还会保存数组的长度。b)
实例数据。类中定义的成员变量。规则:先放父类的成员变量,在放子类的成员变量。相同宽度的变量被放在一起。如果compactFileds
参数为true
,子类的窄变量可能插入到父类变量的空隙。c)
对齐填充
2.如下图所示,比较详细的解释了对象的内存布局以及它和栈、方法区之间的联系。
虚拟机栈中保存的是一个个栈帧,栈帧中是一个个成员变量,其中cust
成员变量指向了堆空间中创建的Customer()
实例,对象实例包含对象头、实例数据、以及对齐填充。
其中对象头又包含运行时元数据、类型指针;运行时元数据包含哈希值、GC
分代年龄、锁状态标志等;类型指针指向方法区中Customer
对象的类型信息。
实例数据包含的是类Customer
中定义的成员变量信息。其中name
是一个字符串常量,指向了字符串常量池中的字符串常量(注意,字符串常量池在JDK7
就已经被移动到了堆区);acct
是一个对象的引用,又指向了一个堆区中新的对象。
最后一部分对齐填充,主要是为了内存字节对齐。
2.对象的访问定位
对象访问的方式有两种:句柄访问和直接指针。
1.句柄访问。
Java堆中将会划分出一块内存来作为句柄池,reference
中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。
2.直接指针(HotSPot
虚拟机采用)。reference
中直接存储的就是对象地址,对象中存储了到对象类型的类型指针。
优缺点:
1.使用句柄的方式,需要开辟一块独立的空间给句柄池;同时访问对象效率较低,需要通过两次指针。句柄稳定,对象被移动只要修改句柄中的地址。
2.使用直接指针的方式,访问速度快,节省了一次指针定位的开销