仅讨论创建普通的Java对象,不包括数组、Class对象等
1.类加载校验
虚拟机遇到new指令时,首先会去检查这个符号引用代表的类是否已经被加载、解析和初始化过(方法区中是否存在)。如果没有那必须先进行类加载过程。
2.分配内存
对象所需要的内存的大小在类加载之后就能完全确定。为对象分配空间的任务等同于将一块确定大小的内存空间从Java堆中划分出来。
指针碰撞(Bump the Pointer)分配内存
假设所有的Java堆内存都是绝对规整的。通过一个指针来区分已分配的内存和未分配的内存,每次分配内存都是移动指针。这种分配内存的方式叫做指针碰撞。
空闲列表(Free List)分配内存
假设所有的Java内存不是绝对规整的。虚拟机就需要通过一个表记录未分配的内存块,分配的时候在表中找到一个满足的内存块,并更新表。
Java堆的内存是否规整由所采用的垃圾收集器是否带有压缩整理功能决定。在使用Serial、ParNew等带Compact过程的收集器中,采用的是指针碰撞分配内存;如果使用的是CMS这种基于Mark-Sweep算法的收集器时,使用的是空闲列表分配内存。
除了如何划分可用空间之外,还需要考虑创建对象的并发问题。解决这个问题由两种方式:
- CAS失败重试保证原子性
- 将内存分配的动作按线程划分在不同的空间之中(线程隔离)
采用线程隔离的时候,每个线程预先分配的空间叫做本地线程分配缓冲(Thread Local Allocation Buffer TLAB)。当TLAB用完的时候,需要同步锁定分配,并分配新的TLAB。虚拟机是否使用TLAB,可以通过-XX:+-UseTLAB参数来设定
3.初始化内存空间为零值
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),如果使用TLAB,这一过程可以提前到TLAB分配对象内存空间时进行。这一步操作保证了对象的实例字段在不赋初值时就能够直接使用。
4.设置对象的对象头
设置这个对象的类信息、类的元数据信息、对象的哈希吗、对象的GC分代年龄等信息,将他们存放到对象头中。根据虚拟机当前的运行状态的不同(如是否开启偏向锁),会对对象头有不同的设置方式。
5.对象的初始化
对于JVM来说,对象已经创建完毕,但对于Java程序来说,对象创建才刚刚开始。这时所有的字段都为零值。这时对象会执行init方法,初始化对象。