从JVM分析对象创建的过程
如图:
1.类加载检查
虚拟机遇到一条new指令时,首先去检查这个指令的参数是否能在称量翅中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,先对这个类进行加载,然后进行下一步。
Java常见创建对象的方式:
1、new对象
2、clone对象
3、序列化创建对象
4、反射创建对象
5、单例singleton new Instance
2.分配内存
类加载完成后,虚拟机将为新生对象分配内存。对象需要内存的大小在类加载完成之后就可以完全确定,为对象分配空间的任务等同于加你个一块确定大小的内存从Java堆中划分出来。
这个步骤中有两个关键点
1、如何划分内存
划分内存的方式有两种
(1)一种是指针碰撞Bump the Pointer(JVM默认采用这种方式),如果Java堆中的内存是规整的,即使用过的内存在一边,未使用的在另一边,中间放着一个指针作为分界点的指示器,那么内存分配就是指针的移动;
(2)另外一种方式是空闲列表Free LIst,使用的场景比如说采用标记清除的GC算法会产生大量的内存碎片。这个时候无法使用指针碰撞的方式,这时虚拟机会维护一个列表,记录哪些内存是可用的,在分配的时候再列表中找出一块足够大的空间分配给对象实例,并更新列表上的记录。
2、在并发情况下,对象之间可能存在内存竞争,出现指针抢占的问题。
解决并发问题的方法:
(1)CAS(compare and swap) 通俗理解:大家去抢同一个内存,没抢到的继续重试下一块内存空间
虚拟机采用CAS配上失败重试的方式保证更新操作的原子性来对分配内存空间的动作进行同步处理。
(2)本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
每个线程在Java堆中预先分配一小块内存。通过-XX:+/-UseTLAB参数来设定虚拟机是否使用TLAB(JVM会默认开启-XX:+UseTLAB),-XX:TLABSize 指定TLAB大小。
3.初始化零值
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头), 如果使用TLAB,这一工作过程也可以提前至TLAB分配时进行。
作用:保证了对象的实例字段在Java代码中可以不赋初值就可以直接使用,程序能访问这些字段的数据类型所对应的零值。
4.设置对象头
初始化零值之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头Object Header之中。
对象头在hotspot的C++源码markOop.hpp文件里的注释如下:
cpp
// Bit-format of an object header (most significant first, big endian layout below):
//
// 32 bits:
// --------
// hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
// size:32 ------------------------------------------>| (CMS free block)
// PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
// 64 bits:
// --------
// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
// PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
// size:64 ----------------------------------------------------->| (CMS free block)
//
// unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object)
// JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object)
// narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
// unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
5.执行方法
对象按照程序中的设置值进行初始化,区别于第步的初始化零值,这个操作流程是把实际的值赋值给对象。