Object obj = new Object();
Java中创建对象时,需要经过5个阶段,分别是:类加载检查、分配内存、初始化零值、设置对象头、执行init方法。
1、类加载检查
当JVM执行到一条new指令时,先检查这条指令的参数是否能在常量池定位到这个类的符号引用,同时检查这个类是否已被加载、解析、初始化,如果没有则会先进行类加载过程。
2、分配内存
在类加载检查后,JVM为新对象分配内存。对象所需的内存大小在类加载完成后可确定,因此分配内存的过程就是把一块确定大小的内存从堆中划分出来。分配方式有指针碰撞和空闲列表两种,选择哪种分配方式由堆内存是否规整决定,堆内存是否规整又由垃圾收集器决定。
指针碰撞:适用于堆内存规整的情况,由于堆中将已被使用过的内存整合到一边,未使用的内存整合到另一边,中间有一个分界指针,所以分配内存时只需向未分配内存的方向移动对象所占内存大小位置即可。针对标记-整理算法的垃圾收集器。
空闲列表:适用于堆内存不规整的情况,JVM会维护一个列表,列表中记录哪些内存块可用。分配内存时,找到一块足够大的内存块来分配给新对象,然后更新列表。针对标记-清除算法的垃圾收集器。
那么内存分配的并发问题如何解决?
JVM通过两种方式保证内存分配的安全性:
CAS:在分配内存时,JVM会采用CAS+失败重试的方式保证更新操作的原子性
TLAB:给每个线程都先在Eden区分配一块私有内存区域,JVM在给线程中对象分配内存时,先在TLAB分配,当对象大于TLAB的剩余内存或TLAB内存用尽时,再采用CAS的方式在共享内存上分配。
3、初始化零值
内存分配完成后,JVM要将分配的内存空间初始化为零值,这一步也保证了对象的实例字段能不赋初始值就直接使用,程序访问到的是这些字段对应数据类型的零值。
4、设置对象头
初始化零值后,JVM会设置对象的对象头信息,包括:Mark Word和类型指针。Mark Word包含对象的哈希码、GC分代年龄、锁信息等,类型指针指向方法区中类的元数据信息。
5、执行init方法
按照代码中指定的初始化方法对对象进行初始化。