一、对象的创建逻辑
二、对象的内存布局
2.1 对象头
Mark Word(标记字段)
:大小是8个字节
。默认存储的是对象的hashCode
、锁状态标识
以及GC分代年龄
。根据对象的状态复用自己的存储空间,即在运行期间Mark Word里的数据会随着锁状态标志位的变化而变化。Klass Point(类型指针)
:大小是4个字节
。对象指向它的类元数据的指针
,虚拟机通过这个指针来确定对象是哪个类的实例- 若为
对象数组
,会存放对象的长度。
对象头扩展:
- 通过
java ClassLayout.parseClass
(对象.class),可获得对象头的具体信息; - 整个对象的大小为
java 16个字节
(在64为虚拟机上对象的大小必须为8的倍数)。对象头的大小为12字节,其中MarkWord大小为8字节,Klass Word为4个字节;为什么上图中Klass Word为64bit呢?原因:进行了```指针压缩````。 8个字节
。后七个字节存储hashcode信息,其中hashcode原本是不显示出来的,只有当调用对象的hashcode方法时,才会显示出来(即hashcode是计算出来的);第一个字节用于存放锁状态标识、gc年龄和对象状态。从上图中,可以得出以下信息:
疑问1: 分代年龄为什么为15?,答:因为4位存储的最大数据就是15;
其中偏向占用一位,用来判断是否加锁。当偏向对应的那一位为0时,说明没有加锁,对应对象状态为无锁状态(0 00),当偏向对应的那一位为1是,说明加锁了,分为5个状态,无锁、偏向锁、轻量级锁、重量级锁以及GC标志。
2.2 实例数据
存储类的数据信息
、父类的信息
(大小和存储的数据有关)。
2.3 对齐填充
不是必须存在的,因为虚拟机要求对象起始地址是8字节的整数倍
,仅仅是为了对齐(空对象的大小为8字节,因为会对齐填充)。
三、对象的访问定位
使用对象的时候,Java程序会通过栈上的reference数据
来操作堆上的数据。通过什么方式定位访问堆中的对象,是由具体的虚拟机决定的。
3.1 句柄访问
reference指向的是对象的句柄地址
3.2 直接指针访问
reference指向的是对象地址
3.3 两者区别
句柄方式:
因reference中指向的是对象稳定句柄地址,对象被移动时,只会改变句柄池中实例数据指针,而reference不需要被修改;解耦
直接指针方式:
速度更快,节省了一次指针定位的时间开销。Hotspot默认使用的是直接指针方式。