前面介绍了jvm运行时数据区域后,下面讲解下对内存中数据的其他细节,看他们是如何创建、布局及访问的
一、对象的创建
1.对象的分配
对象的创建分配方式主要有两种:指针碰撞和空闲列表
指针碰撞:
假设堆内存中是绝对规整的,那么,在为新对象分配内存空间时,只需要将指针向空闲空间方向移动新对象
所需大小的一段出来即可。
一般使用带有compact(整理)过程的收集器时,使用指针碰撞
空闲列表:
如果内存不是规整的,这时就需要维护一个列表,记录哪些内存是空闲的,在分配空间时,从列表中找出一块
足够大的空间划分为对象实例并更新列表记录
使用基于mark-sweep算法的收集器时,使用空闲列表
2.对象的初始化及设置
初始化:内存分配完成后就是对对象进行初始化,虚拟机将初始化的内存空间都初始化为0值,
这就是为什么对象的实例字段在java代码中不需要初始化也可以使用
设置:之后,虚拟机对对象进行一些必要设置,主要是往对象头中存入类的元数据信息、对象的哈希值、分代年龄等。
init: 经过上面两步后对象创建完成,但所有字段还都是0值。最后就是执行init方法,按程序员的意思初始化对象
完成上述三步后,一个对象就算创建完成。
二、对象的内存布局
对象在内存中存储的布局主要分为三部分,对象头、实例数据、对齐填充
对象头:
对象头在对象设置阶段提到过,即在设置阶段会向对象头中存入一些类的元数据信息、哈希值、分代年龄等
对象中主要分为两部分:运行时数据和类型指针
运行时数据:主要存储上面对象设置阶段存储的哈希码、分代信息、锁状态标识、线程持有的锁等。
类型指针:主要存储对象的类元数据的指针,即这个对象是哪个类的实例,此外,如果对象类型是数组,
对象头中还会存储一个代表数据长度的数据
对齐填充:不是必然存在的,在Hotspot中,要求对象起始地址必须是8字节的倍数,
当对象实例部分没对齐时,用对齐填充来补全。
三、对象的访问定位
java程序是通过栈上的reference数据来操作对象实例的,因此它只是一个引用,具体定位方式jvm有不同实现
主流实现方式有两种:使用句柄和直接指针
句柄池:
原理:java堆中会划出一片区域作为句柄池,reference存储的就是句柄地址,
里面包含对象实例数据的指针和对象类型指针
好处:如果对象被移动,只需改变句柄池中存储的地址
直接指针
原理:如果使用的是直接指针,那么reference中存储的就是对象的地址
好处:省去了一次定位的时间,效率更高
Hotspot使用的是直接指针方式