前言
梳理对象创建流程和初始化流程
对象创建流程
- new指令时,定位该指令的参数在常量池中的符号引用
- 遇到new、getstatic、putstatic、invokestatic这4条字节码指令时,
- 使用java.lang.reflect包的方法对类进行反射调用时。
- 当初始化一个类时,发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,虚拟机会先初始化包含main()方法的主类。
- 被动引用不会触发类的初始化:
- 子类引用父类的静态字段,不会导致子类初始化。
- 通过数组定义来引用类,不会触发此类的初始化。new ChildClass[0]。
- 引用类中的常量,不会触发此类的初始化。
- 如果没有,则进行类的加载、连接和初始化。
- 虚拟机为新生对象分配内存
- 将分配到的内存空间都初始化为零值,不包括对象头,并初始化对象头(哈希码、gc年龄等)
- 调用对象<init>方法
另外new对象操作不是原子性的,
- 给Singleton实例分配内存
- 调用Singleton()构造函数,进行初始化
- 将mSingle对象指向分配的内存空间
因为按照内存模型中线程工作内存回写到主内存时2、3的步骤是不确定的,当3在前面发生时,会引发DCL(Double check Lock)失效问题,可以使用volatile禁止指令重排,保证按顺序执行,直接从主内存读写,就不会出错了。
1. 类加载流程
2. 对象的内存布局
对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。
1. 对象头
Hotspot虚拟机的对象头主要包括两部分数据:Mark Word(标记字段)、Klass Pointer(类型指针),数组会多1字宽(32位: 4字节)来存储数组长度。
- Mark Word用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键。
- Klass Point是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例;
2. 实例数据
该部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容,包括父类继承下来的和子类中定义的。
3. 对齐填充
该部分不是必须的,某些虚拟机要求对象的起始地址必须是8字节的整数倍,即对象大小必须是8字节的整数倍,所以不足时就需要对齐。
4. Mark Word详解
JVM中对象头的方式有以下两种(以32位JVM为例):
// 普通对象
|--------------------------------------------------