题目
new 一个对象在堆中的历程(对象的创建过程)
答案
1)**类加载检查**:当 Java 虚拟机遇到一条字节码 new 指令时,它会首先检查根据 class 文件中的常量池表(Constant Pool Table)能否找到这个类对应的符号引用;然后去**方法区中的运行时常量池**中查找**该符号引用所指向的类是否已完成了类加载过程(加载、验证、准备、解析、初始化)**
- 如果没有,那就先执行相应的类加载过程
- 如果有,那么进入下一步,为新生对象分配内存
> **常量池表 (Constant Pool Table)**:
>
> 用于存放**编译期**生成的各种字面量(字面量相当于 Java 语言层面常量的概念,如文本字符串,声明为 final 的常量值等)与符号引用。有一些文章会把 class 常量池表称为**静态常量池**。
>
> 都是常量池,常量池表和方法区中的运行时常量池有啥关系吗?运行时常量池是干嘛的呢?
>
> **运行时常量池可以在运行期间将 class 常量池表中的符号引用解析为直接引用**。简单来说,class 常量池表就相当于一堆索引,运行时常量池根据这些索引来查找对应方法或字段所属的类型信息和名称及描述符信息
2)**分配内存**:就是在堆中给划分一块内存空间分配给这个新生对象用。具体的分配方式根据【堆内存是否规整】有两种方式:
- 堆内存规整的话采用的分配方式就是【指针碰撞】:所有被使用过的内存都被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,分配内存就是把这个指针向空闲空间方向挪动一段与对象大小相等的距离
- 堆内存不规整的话采用的分配方式就是【空闲列表】:所谓内存不规整就是已被使用的内存和空闲的内存相互交错在一起,那就没有办法简单地进行指针碰撞了,JVM 就必须维护一个列表,记录哪些内存块是可用的,在分配的时候从列表中找到一块足够大的连续空间划分给这个对象,并更新列表上的记录,这就是空闲列表的方式
3)**初始化零值**:对象在内存中的布局可以分为 3 块区域:对象头、实例数据和对齐填充,对齐填充仅仅起占位作用,没啥特殊意义,「初始化零值这个操作就是初始化实例数据这个部分,比如 boolean 字段初始化为 false 之类的」
4)**设置对象头**:这个步骤就是设置对象头中的一些信息,比如偏向锁之类的
5)**执行 init 方法**(就是构造函数):最后就是执行构造函数,构造函数即 Class 文件中的 `<init>()` 方法,一般来说,new 指令之后会接着执行 `<init>()` 方法,按照构造函数的意图对这个对象进行初始化,这样一个真正可用的对象才算完全地被构造出来了
