JVM 创建对象的过程
JVM 在创建对象时主要经历以下步骤:
1. 检查类的加载状态
JVM 会检查指令中引用的类是否已被加载、解析和初始化。
如果类未被加载,则触发类加载过程,完成加载、链接和初始化。
2. 分配内存
JVM 会在堆中为对象分配内存空间。
分配方式取决于堆的状态:
指针碰撞(Bump-the-pointer):
适用于堆内存是规整的情况(所有已分配和未分配内存分布在两侧)。
JVM 使用一个指针记录当前可用的内存地址,分配时将指针向前移动分配内存。
高效,但需要堆经过垃圾收集后保持规整。
空闲列表(Free-list):
适用于堆内存存在碎片的情况(内存分布不连续)。
JVM 维护一个空闲列表,记录所有未分配内存块。分配时遍历空闲列表,找到足够大的块。
并发内存分配的安全性:
CAS + 失败重试:利用原子操作确保多线程分配内存的安全性。
Thread Local Allocation Buffer (TLAB):为每个线程分配一个私有内存区域,用于分配小对象,减少锁竞争。
3. 内存初始化
JVM 将分配到的内存空间初始化为零值,确保对象字段具有默认值:
基本类型:
int
为0
,boolean
为false
等。引用类型:初始化为
null
。
4. 设置对象头
对象头存储了对象的元数据信息,包括:
Mark Word:存储对象的哈希值、GC 分代年龄、锁状态等信息。
类指针:指向对象所属类的元数据。
数组长度(仅数组对象)。
5. 执行构造方法
调用类的构造方法,按照代码中的逻辑为字段赋值。
包括显式初始化和静态代码块的执行。
6. 返回对象引用
对象创建完成后,返回对象的引用,供程序使用。
知识扩展
1. 指针碰撞 vs 空闲列表
指针碰撞:
高效,但需要垃圾收集器整理堆空间。
适用于 复制算法、标记整理算法。
空闲列表:
更灵活,适用于内存碎片较多的情况。
常见于 标记清除算法。
2. JVM 如何保证线程安全
TLAB:每个线程有独立的内存分配区域,减少竞争。
CAS:通过原子性操作分配内存,确保分配过程线程安全。
3. 大对象的分配
大对象(如大数组、长字符串)通常直接分配到老年代,避免在新生代中频繁移动。
总结
JVM 创建对象的过程包括检查类加载状态、分配内存、初始化、设置对象头和调用构造方法等。
通过优化分配方式(指针碰撞、空闲列表)和线程安全机制(TLAB、CAS),JVM 能够高效且安全地完成对象创建过程。