JVM学习总结之-HotSpot虚拟机对象探秘

在初步了解了内存模型之后,我们需要继续了解在Java堆中,HotSpot虚拟机是怎么定义对象的内存分配,布局和访问的。

对象的创建

  1. 当Java虚拟机遇到一条new指令的时候,会先检查这个指令的参数在常量池中是否能找到对应的符号引用,如果找到了,再判断该符号引用对应的类是否已经被加载、解析、和初始化过,如果没有,则会执行相应的加载流程。
  2. 在类加载检查通过之后,虚拟机会为新生对象分配内存,对象所需内存空间在类加载完毕之后便可以完全确定。
  3. 内存分配完成之后,虚拟机会对分配到的内存空间(对象头除外)都初始化为零值,这步操作保证了对象的实例字段在Java代码中不赋初值的情况下也能正常访问这些字段对应的零值。
  4. 接下来,Java虚拟机还会对对象进行必要的设置,比如是哪个类的对象、如何找到类的元数据信息、对象的哈希码、对象的GC分代年龄等,这些内容都存在对象头(Header)中。
  5. 从上面的工作完成后,在虚拟机的角度上看,一个新的对象已经产生,但是在Java程序角度看,工作才刚开始–构造函数,即Class文件中的init()方法还没执行,init()方法在new指令之后执行,该方法会根据代码的意愿对对象进行初始化,当init执行完毕之后,一个对象才算正在生成。

对象的内存布局

对象内存布局图总体如下:

对象内存
对象头
运行时数据
哈希码
GC分代年龄
锁状态标志
线程持有的锁等
类型指针
实例数据
对齐填充

对象的内存主要包含三个部分,分别是对象头(Header)实例数据对齐填充

对象头(Header)
  1. 对象头包含白那个部分信息,第一部分是存储对象自身运行时的数据,如HashCode、GC分代年龄、锁状态标识,线程持有的锁等。
  2. 对象头包含的另一部分则是类型指针,即对象指向它的类型元数据的指针,Java虚拟机通过该指针确定对象是属于哪个类的实例(并不是说有的虚拟机实现都会在对象数据上保存类型指针,换句话说就是查询对象的元数据不一定都需要对象本身),如果对象是一个数组类型,那对象头还需要有一块用来记录数组长度的数据。
实例数据
  1. 实例数据部分是对象真正存储的有效信息,包括程序中定义的各种类型的字段内容,无论是从父类中继承下来的还是子类中定义的字段都需要记录下来。
  2. 默认的分配策略中,相同宽度的字段总会被分配到一起存放,如果虚拟机参数+XX
    :CompactFields参数为true时(默认为true),那子类中类型较小的变量也允许插入父类变量的空隙中。
对齐填充

对齐填充不是必然存在的,也没有特殊意义,仅仅是起着占位符的作用,由于HostSpot虚拟机内存自动管理系统要求对象起始地址必须是8字节的整数倍,换言之对象的内存时8字节的整数倍,因此,如果实例数据部分没有对齐的话,需要对齐填充部分来补全。

对象的访问定位

通过句柄访问对象

通过句柄访问对象,Java堆中会划分一部分空间来作为句柄池,此时栈上的reference存放的是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息。如图:
JVM学习总结之-HotSpot虚拟机对象探秘_第1张图片

通过直接指针访问对象

通过直接指针访问对象,reference中直接存的就是对象的地址,当单纯访问对象本身的时候,可以直接访问,不需要多一次指针定位,如图:
JVM学习总结之-HotSpot虚拟机对象探秘_第2张图片

两种访问方式对比

这两种访问方式各有优势,通过句柄访问的方式最大的好处就是reference直接存的是对象的句柄,当对象移动的时候(GC),只需要改变句柄中的实例数据指针即可,reference不需要改动。
通过直接指针访问的方式最大的好处就是速度快,它节省了一次指针的定位的时间开销,对于HotSpot虚拟机来说,主要用的是直接指针的方式访问。

你可能感兴趣的:(java,java,jvm)