高 进阶
对象创建过程#
一句话答案#
对象创建五步:类加载检查→内存分配(指针碰撞/空闲列表 + TLAB)→零值初始化→设置对象头→执行构造方法。
核心要点
完整流程(5 步):
- 类加载检查:检查 new 指令的参数是否能在常量池中找到类的符号引用,且该类已加载/解析/初始化
- 内存分配:
- 指针碰撞:内存规整时(Serial/ParNew),移动指针分配
- 空闲列表:内存不规整时(CMS),从列表中找合适块
- 并发安全:TLAB(Thread Local Allocation Buffer)或 CAS+失败重试
- 零值初始化:内存清零(保证字段有默认值)
- 设置对象头:Mark Word(哈希/GC年龄/锁状态)+ 类型指针
- 执行
<init>:调用构造方法,按程序员意愿初始化
TLAB(面试加分点):
- 每个线程预分配一块私有内存(Eden 区的一小块)
- 分配对象时优先在 TLAB 中分配,无需同步
- TLAB 用完再 CAS 竞争分配新 TLAB
面试回答(2分钟版)
JVM 中 new 一个对象要经历五个步骤。第一步是类加载检查,JVM 检查 new 指令对应的类是否已经被加载、解析和初始化,如果没有就先触发类加载。第二步是内存分配,根据堆内存是否规整选择指针碰撞或空闲列表方式分配空间。并发环境下为了保证线程安全,JVM 采用 TLAB 机制,即每个线程在 Eden 区预先分配一块私有内存,分配对象时优先在 TLAB 中进行,无需加锁同步,TLAB 用完再通过 CAS 竞争分配新的 TLAB。第三步是零值初始化,把分配到的内存空间清零,这就是为什么实例变量不赋值也有默认值。第四步是设置对象头,包括 Mark Word 中的哈希码、GC 分代年龄、锁状态标志以及类型指针。第五步才是执行 init 构造方法,按照程序员编写的逻辑完成初始化。TLAB 是一个容易被忽视的面试加分点,它解释了高并发下对象分配为什么还能很快。
追问与易错
追问方向:
- “对象分配在堆上还是栈上?”→ 默认在堆上分配;如果 JIT 逃逸分析判定对象不逃逸出方法,可以通过标量替换将对象拆解为局部变量在栈上分配,方法结束自动回收无需 GC
- “TLAB 是什么?为什么需要?”→ Thread Local Allocation Buffer,每个线程在 Eden 区预分配的私有内存块,对象分配优先在 TLAB 中进行无需加锁,避免多线程竞争堆内存分配的同步开销
- “new 和构造方法的关系?”→ new 指令负责类加载检查+内存分配+零值初始化+设置对象头,构造方法(init)是最后一步按程序员逻辑初始化字段;new 之后构造方法之前字段是零值
易错点:
- ❌ new 完对象就能用了——构造方法执行前字段是零值
- ❌ 忽略并发分配的线程安全问题