Prerequisite:内存布局和类加载流程
要理解一个对象的创建过程,需要从运行时数据区进行分析,首先需要对JVM运行时数据区布局有深入的理解,同时掌握类加载过程中各个阶段的行为。
Tip:实践结合理论
梳理new 对象的过程,也是间接从实践的角度去复习了JVM 运行时数据布局和类加载流程两个大主题,后者以前者为基础。
简化流程解析
当虚拟机遇到一个 new 指令时:
1、在常量池中检查
在常量池中检查是能找到一个当前类的符号引用,并检查该符号引用代表的类是否已经被加载、解析和初始化过。
2、类加载检查通过
3、在堆中为这个类的对象分配确定大小的内存块,内存大小在类加载完成之后,就已经确定下来。
4、JVM 将分配到的内存空间都初始化为零(不包括对象头)。
5、设置对象头。对象头中存储了该对象是哪个类的实例等信息。
6、执行 init 方法(否则所有字段还为零值),把对象按照程序员的意愿进行初始化。
简化流程示意图
实例分析
代码编译完,有TestClient.class 和 Person.class 两个类文件。 JVM 读取 main 方法入口,发现 Person p = new Person(); 这个需要创建对象的语句所对应的字节码指令,执行如下操作:
1、加载 Person.class 文件到方法区,同事加载 Person 类中的 static 属性 2、在 main 方法所在的栈区分配引用 p 3、在堆中开辟空间存放Person 类,但是不进行初始化操作 4、初始化数据字段 5、将引用 p 指向 Java 中新开辟的 Person 类
对象的内存布局
对象在堆内存中,存储布局分为三部分:
对象头(Object Header)
实例对象(Instance Data)
对齐填充(Padding)
对象头
对象头包含2部分信息:
1、存储对象自身的运行时数据:哈希吗、GC分代年龄、锁定态标识、线程持有锁等
2、指针类型:即对象指向它的类元数据的指针。JVM 通过这个指针类确定这个对象属于哪个类的实例。