一、对象的创建
之前一篇详细分析过类的加载,本篇主要记录分析对象的创建步骤以及jvm内存的分配。
直接上图
显而易见,对象的创建大致经过7个步骤:
1.类是否加载判断,如果想要创建一个类的实例对象,首先这个类是要被加载完成之后才可以,所以第一步就要判断类是否被加载过,若未被加载,则先加载类。
2.内存分配,创建对象之前首先需要在堆中分配一块足够大的内存空间,具体多大,这个在对应的类加载完成之后,jvm就已经可以确定这类的对象需要占用多大的内存了,所以只要在堆内存中划分一块确定的内存空间即可。内存分配的方式有两种:
- 指针碰撞法:堆内存中的空间比较规整,所有已经使用的内存空间规整的排列在一起,然后使用一个指针把已使用的内存空间和未使用的内存空间分隔开来,当一个对象需要分配内存时,只需要把指针向后移动对象需要的大小即可,jvm默认使用这种方法。
- 空闲列表法:对象在堆内存中的分配比较随意凌乱,所以jvm在内部维护了一个可用内存列表,当一个对象需要分配内存时,在空闲列表找出一块足够大的内存给新生对象,然后这块内存从空闲列表中移除。
内存分配的并发问题:
不管使用哪种分配方式都有并发问题,例如:使用指针碰撞法分配内存空间,A对象和B对象同时获取到指针,给A分配内存后,B又使用老的指针进行分配。
解决方案:
- 使用CAS+重试的方式分配内存,在内存分配时,使用CAS原子操作进行内存的分配,如果CAS操作失败的就进行重试 一直到成功为止,这种方式性能较低。
- 本地线程缓冲(Thread Local Allocation Buffer,TLAB),这堆中种方式就是预先在给线程分配一块空间,之后每个线程需要创建对象,就在属于自己的堆内存空间中分配,这样就避免了并发分配问题,这种是JVM默认的解决并发分配的方式,可以使用配置参数:-XX:+/-UseTLAB配置是否开启此方法(默认开启),-XX:TLABSize配置TLAB的大小。
3.初始化
在堆中给新生对象分配好内存空间之后,虚拟机把分配到的这块内存空间都初始化为零(不包括对象头),如果使用了TLAB,这一过程也可以提前至TLAB分配时进行,这样可以保证对象的实例在java代码中不赋初始值就可以使用,程序可以访问到这些字段的零值。
4.设置对象头
java对象包括三个部分,对象头,实例数据区,对齐填充位,其中对象头中保存了对象的hashCode,分代年龄,锁的标志位信息,这一块被称为mark word,除此之外还有类元信息指针,针对数组对象还记录了数组长度等信息,所以一个对象被创建成功前是需要设置对象头的信息的,换句话说,就是把对象的hashCode、分代年龄,类元信息指针都赋好值。
针对对象头这一块做一个特别说明:
对象头也有三块组成:
- mark word:在32位操作系统中,占4个字节 ,在64位操作系统下占8个字节,如下
锁状态
25bit
4bit
1bit
2bit
23bit
2bit
是否偏向锁
锁标志位
无锁
对象的HashCode
分代年龄
0
01