上篇博客介绍的对象的创建过程,本文来介绍一下对象的组成结构。
在HotSpot
虚拟机中,对象在内存中的布局划分为3
个区域:对象头(Header
),实例数据(Instance Data
)以及对齐填充(Padding
)。
对象头
HotSpot
虚拟机对象的对象头一般包含两部分信息。
- 第一部分用于存储对象自身的运行时数据,例如
HashCode
、GC
分代年龄等信息。在32
位和64
位的JVM
中,这部分数据分别为32bit
和64bit
,官方称这部分数据为Mark Word
。 - 另一部分用于存储对象的类型指针,该指针指向它的类元数据的指针,
JVM
通过这个指针来确定对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说,查找对象的元数据信息并不一定要经过对象本身,在后面的博文中将进行讨论。
另外,如果对象是一个Java数组,那么对象头中还必须有一块用于记录数据长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数据的元数据中却无法确定数据的大小。
之前提到对象头一般包含两部分信息,这是因为如果对象是一个数组,那么对象头还需要有额外的空间用于存储数组的长度,并且这部分数据也随着JVM
位数的不同而不同:32
位的JVM
上,该区域的长度为32bit
,在64
位未开启压缩指针的JVM
中,这部分数据的长度为64bit
,否则为32bit
。
实例数据
实例数据部分是对象真正存储有效信息的区域,存储了代码中定义的各种字段的内容,包括从父类继承下来的字段和子类中定义的字段。
实例数据紧随对象头,为了提高存储空间的利用率,这部分数据的存储顺序会受到虚拟机分配策略参数(FieldsAllocationStyle)和字段在Java
源码中定义顺序的影响。
HotSpot
虚拟机默认的分配策略如下所示:
doubles & longs
ints & floats
shorts & chars
booleans & bytes
references
可以看出,相同宽度的字段总是被分配到一起,并且在满足这个条件的前提下,在父类中定义的字段会出现在子类字段之前。
对齐填充
对齐填充这部分不是必须存在的,这部分仅仅是起着占位符的作用。由于HotSpot
虚拟机的自动内存管理系统要求对象的起始地址必须是8
字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或两倍)。因此当对象实例部分数据没有对齐时,就需要对剩余的部分进行填充。