java对象的创建过程

对象的创建可分为以下几步:
1:检查类是否已经被加载;
虚拟机遇到一条new指令时,首先检查这个指令的参数能否在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,则先进行相应的类的加载过程。

2:为对象分配内存空间;
在类加载检查完成后,接下来虚拟机为新生对象分配内存。对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定的内存从java堆中划分出来。主要有两种方式:

(1)指针碰撞
jvm将堆区抽象为两块区域,一块是已经被其他对象占用的区域,另一块是空白区域,中间通过一个指针进行标注,这时只需要将指针向空白区域移动相应大小空间,就完成了内存的分配,当然这种划分的方式要求虚拟机的对内存是地址连续的,且虚拟机带有内存压缩机制,可以在内存分配完成时压缩内存,形成连续地址空间,这种分配内存方式成为“指针碰撞”,但是很明显,这种方式也存在一个比较严重的问题,那就是多线程创建对象时,会导致指针划分不一致的问题,例如A线程刚刚将指针移动到新位置,但是B线程之前读取到的是指针之前的位置,这样划分内存时就出现不一致的问题,解决这种问题,虚拟机采用了CAS配上失败重试的方式来保证更新操作的原子性;
java对象的创建过程
(2)空闲列表
为了解决第一种分配方式的不足而创建的方式,多线程分配内存时,虚拟机为每个线程分配了不同的空间,这样每个线程在分配内存时只是在自己的空间中操作,从而避免了上述问题,不需要同步。当然,当线程自己的空间用完了才需要需申请空间,这时候需要进行同步锁定。为每个线程分配的空间称为“本地线程分配缓冲(TLAB)”,是否启用TLAB需要通过 -XX:+/-UseTLAB参数来设定。

3:为对象字段设置零值;
分配完内存后,需要对对象的字段进行零值初始化(对象头除外),如果使用TLAB,这一工作过程也可以提前至TLAB分配时进行。零值初始化意思就是对对象的字段赋0值,或者null值,这也就解释了为什么这些字段在不需要进程初始化时候就能直接使用;

4:设置对象头;
接下来,虚拟机对对象进行必要设置(如对象的哈希码,对象的GC分代年龄,该对象是哪个类的实例,如何才能找到类的元数据信息等),这些信息存放在对象的对象头中。

5:执行init方法。
此时,从虚拟机角度看,新的对象已经产生,但从java程序角度看,对象创建才刚开始,执行new指令后会接着执行方法,把对象按照程序员的意向进行初始化,这样一个真正可用的对象才算完全产生出来。