对象内存布局
在Hotspot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头,实例数据,对齐填充。
1.对象头
1.1 存储对象自身的运行时数据(mark word):哈希码,gc分代年龄。
1.2 Class指针:通过该指针确定该对象是哪个类的实例。
在64位系统中,class指针占4B,mark word在开启指针压缩的时候占4B;未开启指针压缩的时候占8B. 在32位系统下,上面两部分各占4B;
2.实例数据
默认分配策略:long/double -> int/float -> short/char -> byte/boolean -> Reference
如果设置了-XX:FieldsAllocationStyle=0(默认是1),那么引用就会放在最前面:Reference -> long/double -> int/float -> short/char -> byte/boolean
结论:分配策略总是按照宽度由大到小的顺序排列,相同宽度的放在一起。
在有继承关系的对象中,只有一个头部,父类的属性分配到该对象中,这样才能通过父类访问他的属性。如果开启CompactFields,那么,子类中较窄的变量就会插到父列的变量的空隙中。
[[注]] 静态属性所占的空间通常不计算到对象本身的空间中,以为他的引用是在方法区。
属性的对齐,例如byte占用一个字节的空间,那么就会填充3字节实现对齐。(32bit系统)
3.对齐填充
Hotspot虚拟机要求java对象起始地址必须是8的整数倍,所有Java对象在内存中将以8字节对齐,也就是对象的总大小必须是8字节的整数倍。
对象的访问定位
程序通过栈上的引用来操作堆上的具体对象,访问方式有两种。
句柄访问方式:java堆中将划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。
指针访问方式:reference变量中直接存储的就是对象的地址,而java堆对象一部分存储了对象实例数据,另外一部分存储了对象类型数据。
这两种访问对象的方式各有优势,使用句柄访问方式最大好处就是reference中存储的是稳定的句柄地址,在对象移动时只需要改变句柄中的实例数据指针,而reference不需要改变。使用指针访问方式最大好处就是速度快,它节省了一次指针定位的时间开销,就虚拟机而言,它使用的是第二种方式(直接指针访问)。