java对象的创建
对象创建的过程:
类加载检查:
- 虚拟机遇到一套new关键字后,首先将去检查这个指令的参数在常量池中能否定位到这个类符号引用,并且检查这个符号引用发表的类是否进行加载过、解析过和初始化过,如果没有将进行类的加载过程。
分配内存:
- 类加载检查完成后,将进行新生对象分配内存,对象所需的内存大小在类加载的过程就已经确定下来。为对象分配内存等同于将确定大小的内存从java堆中划分出来。分配内存的方式有两种,分别是:“指针碰撞”和“空闲列表”,选择哪种方式的依据java堆是否规整,而java堆是否规整又由所才用的垃圾回收器是否带有压缩整理功能决定(标记-整理,复制算法)。
内存分配的两种方式:
适用场合 | 原理 | 垃圾收集器 | |
---|---|---|---|
指针碰撞 | 堆内存规整(即没有内存碎片)的情况下 | 用过的内存全部整合到一起,没用过的内存全部整合到一起,中间有个分界值指针,只需要将向着没用过的内存方向移动对象大小位置即可 | ParNew、Serial |
空闲列表 | 堆内存不规整的情况下 | 虚拟机会维护一个列表,该列表记录哪些内存块是可以使用的,在分配的时候只要找一块足够大的内存块来划分对象实例,更新列表记录 | CMS |
内存分配的并发问题:
虚拟机创建对象是很频繁的事情,虚拟机必须保证线程的安全,故内存分配采用两种方式保证线程的安全:
CAS+失败重试:
- 采用不加锁的方式进行分配内存,如果发生冲突,就会进行重试直到成功为止。
TLAB:
- TLAB(Thread Local Allocation Buffer):为每个线程在Eden区分配一块内存,虚拟机在给线程中的对象分配内存时,首先在TLAB中分配,当对象大于TLAB中剩余的内存或TLAB中的内存不足时,再采用CAS+失败重试饿方式为对象分配内存。
- 初始化零值:
内存分配完成后,虚拟机需要将分配到的内存空间都初始化零值(不包含对象头),这步的作用是保证对象的实例字段在代码中不需要赋初始值就可以进行使用,程序能访问到这些字段所对应的的数据类型的初始值。
- 设置对象头:
初始化零值完成之后,虚拟机要对对象进行必要的设置。,例如这个对象是那个类的实例,如何才能找到类的元数据信息,对象的哈希码,对象的GC分代年龄等,这些信息保存在对象头中。另外根据虚拟机当前运行状态的不同,如是否使用偏向锁,对象头会有不同的设置方式。
- 执行init方法:
在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始, 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。