对象创建过程
1. 类加载检查
虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,必须先执行相应的类加载过程。
2. 分配内存
在类加载检查通过后,虚拟机将为新生对象分配内存。对象所需内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。分配方式有:指针碰撞
、空闲列表
两种。选择那种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的的垃圾收集器是否带有压缩整理功能决定。
- 指针碰撞
- 适用场合:堆内存规整(无内存碎片)情况下
- 原理:用过的内存全部整合到一边,没有用过的内存放到另一边,中间存在一个分界值指针,只需要向没用过的内存方向移动对象内存所需大小位置即可。
- GC收集器:Serial、ParNew
- 空闲列表
- 使用场合:堆内存不规整(有内存碎片)情况下
- 原理:虚拟机会维护一个列表,此列表中记录那些内存块是可用的,在分配的时候,找一块儿足够大的内存块来划分给对象实例,然后更新列表记录。
- GC收集器:CMS
- 垃圾收集器
- 新生代垃圾收集器
- Serial串行收集器【复制算法】
- Serial串行收集器【复制算法】
- 新生代垃圾收集器
Serial收集器是新生代单线程收集器,优点是简单高效,是最基本、发展历史最悠久的收集器。它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集完成。
- PraNew并行收集器【复制算法】
Serial收集器的多线程版本,除了使用多线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The Worl、对象分配规则、回收策略等都与Serial 收集器完全一样。
- Parallel Scavenge并行回收收集器【复制算法】
该收集器的目标是达到一个可控制的吞吐量(Throughput)。良好的响应速度能提升用户体验,而高吞吐量则可用高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
- 老年代垃圾收集器
- Serial Old串行收集器【标记整理算法】:Serial的老年代版本。
- Parallel Old并行收集器【标记整理算法】:Parallel Scavenge的老年代版本,1.6+支持。
- CMS收集器【标记清除算法】:获取最短回收停顿时间为目标的收集器。
- 新生代和老年代垃圾收集器(回收整个Java堆)
- G1收集器【标记整理算法】:1.7+,用于取代CMS收集器。
- 内存分配并发问题
- CAS+失败重试:CAS是乐观锁的一种实现方式,假设在无冲突的场景下完成某项操作,若出现失败进行重试,直到成功为止。
- TLAB:为每一个线程预先在Eden区分配内存,在给线程中对象分配内存时,首先在TLAB进行分配,当对象所需内存大于TLAB剩余内存或TLAB内存已用尽时,采用CAS+失败重试的方式进行内存分配。
3. 初始化零值
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不含对象头),此操作保证对象的实例字段在Java代码中可以不赋初始值就可以直接使用,程序能访问到这些字段的数据类型所对应的零值。
4. 设置对象头
初始化零值完成之后,虚拟机要对对象进行必要的设置(类实例对应关系、类的元数据信息、对象的哈希码、对象的GC分代年龄等信息),这些信息存放在对象头中。
5. 执行init方法
上述工作均完成后,执行new指令后会调用初始化 <init>
方法,完成对象的创建工作