1. 组成
java对象在内存中的储存布局可以分为:对象头(Object Header)
、实例数据(Instance Data)
以及对齐填充(padding)
1.1 对象头(Object Header)
对象头中储存两类信息
- 第一类储存的是
对象运行时的数据(mark word)
,。例如:哈希码(HashCode)
、GC粉黛年龄
、锁标志状态
、线程持有的锁
、偏向锁ID
、偏向时间戳
等,在32和64位虚拟机中分别占32bit
和64bit
,在32位Hot-Spot
虚拟机中,对象未被同步锁锁定的情况下,32bit
储存空间有25bit储存对象的哈希码
、4bit储存分代年龄
、1bit恒为0
、2bit储存锁标志位
Name | Academy | score |
---|---|---|
对象哈希码、分代年龄 | 01 | 未锁定 |
指向锁记录的指针 | 00 | 轻量级锁 |
指向Monitor的指针 | 10 | 重量级锁 |
空,不需要记录信息 | 11 | GC标记 |
偏向线程ID、偏向时间戳、对象分代年龄 | 01 | 偏向锁 |
- 第二类储存的是
类型指针
,即对象指向它的类型的元数据的指针
,虚拟机通过这个指针来确定该对象是哪个类型的实例
注意:如果对象是数组,那么还有一块用于记录
数组长度
的数据,因为普通的java对象可以通过元数据
信息判断对象的大小,而数组长度是不确定的
,无法通过元数据
推断出数组大小。
1.2 实例数据(Instance Data)
实例数据
是对象真正储存的有效信息
,是代码中定义的各种类型的字段内容(包括自己定义的和父类继承的)
,这部分的储存策略受到分配策略参数-XX:FiledsAllocationStyle
和源码定义顺序的影响,默认分配顺序为longs/doubles
、ints
、shorts/chars
、bytes/boolean
、oops
,可以看出宽度相同的字段总是分配到一起存放
,父类的字段总是在子类字段之前
,如果虚拟机参数-XX:CompactFileds
为true(默认就是true)
,那子类中较窄的变量
也可以插入到父类变量的空隙
中去,以节省一点点空间。
1.3 对齐填充(padding)
在Hot-Spot虚拟机的自动内管理系统中要求对象的起始地址
必须是8字节
的整数倍,因此,如果对象的实例数据
所占空间不是8字节
的整数倍,需要进行对齐填充来补全。