根据符号引用判断是否加载、分配内存(指针碰撞或者空闲链表)、初始化零值、设置对象头(实例是哪个类的实例、类的原位置信息、GC 分代年龄)、调用
对象在内存中的存储布局可以分为个区域:对象头、实例数据、对齐填充(非必需)
对象的定位访问有:句柄和直接指针
具体过程:
在常量池中查看是否有 new 的参数对应的类的符号引用,并检查这个类是否被加载解析初始化
加载后,为新对象分配内存,对象所需要的内存大小在类被加载之后就被确定(堆中分配内存:指针碰撞与空闲列表)
将分配的内存空间初始化为零
对对象进行必要的设置(元数据,分代年龄等信息)
执行 <init> 方法,按照程序的值初始化
指针碰撞法:假设 Java 堆中内存是完整的,已分配的内存和空闲内存分别在不同的一侧,通过一个指针作为分界点,需要分配内存时,仅仅需要把指针往空闲的一端移动与对象大小相等的距离
使用的 GC 收集器:serial,parNew
空闲列表法:事实上,Java 堆的内存并不是完整的,已分配的内存和空闲内存相互交错,JVM 通过维护一个列表,记录可用的内存块信息,当分配操作发生时,从列表中找到一个足够大的内存块分配给对象实例,并更新列表上的记录
使用的 GC 收集器:CMS,使用内存不规整的情况下
句柄访问流程如下:
直接访问流程如下:
二者的区别:
这两种对象访问方式各有优势,使用句柄来访问的最大好处就是reference中存储的是最稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改。
而使用直接指针访问方式的最大好处就是速度更加快,它节省了一次指针定位的时间开销,由于兑现搞定访问在Java中非常频繁,因此这类开销积少成多后也是一种非常可观的执行成本。