JVM 中对象的内存布局 和 对象访问定位

1. 对象的内存布局

对象的内存布局主要包括:对象头(Header)、实例数据(Instance Data)和 对齐填充(Padding)。

1.1 对象头(Header)

对象头包括:MarkWord 和  和 类型指针。

MarkWord 是对象自身运行时候的对象头数据,例如:哈希码,GC分代年龄,锁状态标志,线程持有的锁,偏向线程 ID,偏向时间戳。这部分数据在 32 位 和 64位 虚拟机中分别为 32 bit 和 64 bit ,对象在运行的时候数据其实很多,其实已经超出了32bit和64bit,但是对象头信息是与对象自身定义的无关额外存储成本,考虑到虚拟机的空间效率,MarkWord 被设计成为一个非固定的数据结构,以便在极小的空间中存储更多的信息,他会根据自己的状态复用自己的存储空间。

25 bit 存储 hashcode,4bit 存储对象分代年龄,2bit 用于存储锁标志位,1 bit 固定为 0。

类型指针:对象指向他的类元数据(方法区中)的指针,虚拟机通过这个指针确定这个对象是哪个类的实例。

如果对象是一个 Java 数组还应该有一块存储数组长度的数据,因为普通Java对象的元数据信息确定元数据的大小,但是数组无法确定数组的大小。

1.2 实例数据(Instance Data)

实例数据是对象真正存储的有效信息, 也是在程序代码中所定义的各种类型的字段数据,包括从父类继承下来的数据。

实例数据的存储顺序会受到虚拟机的的分配策略影响,分配策略中相同宽度的数据总是放在一块,顺序如:longs/doubles、ints、shorts/chars、bytes、boolean,在满足相同宽度的数据放在一块,父类中定义的变量会出现在子类之前。

1.3 对齐填充(Padding)

HotSpot VM 的自动内存管理系统要求对象的起始地址必须是 8 字节的整数倍,换句话说对象的大小必须是8字节的倍数,如果不够8字节的倍数,就用对齐填充补全,所以说对齐填充不一定存在(对象的大小是8字节的倍数)也没有什么特别的含义。

2. 对象访问定位

Java 程序需要通过栈(虚拟机栈)上的 reference 数据来操作堆上的具体对象,由于reference 数据只是定义了一个指向对象的引用,引用通过何种方式去定位、访问堆中的对象的具体位置,取决于虚拟机,常用的两种方式:使用句柄,直接指针。

使用句柄 这种访问方式,Java 堆中会划分出一块内存作为句柄池, reference 存储的是句柄的指针,句柄包含了对象实例数据和类型数据各自的指针。如下图所示(该图选自:深入理解Java虚拟机:JVM高级特性与最佳实践 / 周志明老师著,下同)。

直接指针 访问方式,Java 堆对象中存储的直接是对象地址,Java堆中需要考虑如何放置访问类型数据的相关信息。

使用句柄 的优势,对象被移动只需修改 句柄池即可,无需修改 reference。

使用直接指针 的优势,访问速度相较于 使用句柄 更快,因为节约了一次的指针定位的开销。

在 Sun HotSpot 中 使用直接指针访问对象。

参考文献

  • 深入理解Java虚拟机:JVM高级特性与最佳实践 / 周志明著. —— 2 版 . —— 北京:机械工业出版社,2013.6 (2019.1重印)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值