对象创建流程
候选人小周在面试美团 P6 时,面试官问道:
"一个对象是怎么创建出来的?new Object() 背后发生了什么?"
小周说:"先分配内存,再初始化..."面试官追问:"内存分配是怎么做的?指针碰撞和空闲列表有什么区别?"
小周答不上来。面试官继续追问:"TLAB 是什么?"
小周彻底卡住了...
一、核心问题:对象创建流程 🔴
1.1 问题拆解
1.2 ❌ 错误示范
候选人原话 A:"对象创建就是分配内存。"
问题诊断:对象创建包括类加载检查、内存分配、初始化、设置对象头等多个步骤。
候选人原话 B:"所有对象都在 Eden 区分配。"
问题诊断:大对象(超过 PretenureSizeThreshold)直接在老年代分配;TLAB 在 Eden 区为每个线程预分配。
1.3 标准回答
P5 级别:完整创建步骤
对象创建的完整流程:
步骤详解:
- 类加载检查:检查常量池中是否有该类的符号引用,检查该类是否已加载/初始化
- 分配内存:根据类元数据计算对象大小
- 初始化为零值:将分配到的内存初始化为零值(除对象头外)
- 设置对象头:设置 Mark Word(hashcode、GC 分代年龄、锁信息)和 Klass Pointer
- 执行构造函数:调用
<init>方法
P6 级别:内存分配方法
指针碰撞(Bump the Pointer):
适用于带压缩整理的 GC 算法(如 Serial、ParNew、G1):
空闲列表(Free List):
适用于不带压缩整理的 GC 算法(如 CMS):
TLAB(Thread Local Allocation Buffer):
为减少并发分配时的竞争,JVM 为每个线程在 Eden 区预分配一块专属缓冲区:
P7 级别:对象访问定位
对象的访问方式:
对象创建后,需要通过 reference(引用)访问对象。JVM 有两种访问方式:
HotSpot 使用直接指针访问:
【面试官心理】 这道题我能问到 P7 级别,是因为对象创建涉及了 GC 算法选择、并发分配优化、对象访问模型等多个维度。能说清 TLAB 原理和指针碰撞/空闲列表差异的候选人说明他理解了 JVM 的内存分配策略。
1.4 追问升级
追问:对象创建过程中,哪些步骤可能抛出异常?
- 类加载检查时:
ClassNotFoundException(如果类未加载)- 内存分配时:
OutOfMemoryError: Java heap space(如果堆不够)- 构造函数中:业务逻辑异常
二、生产避坑 🟡
2.1 大量小对象导致的 GC 压力
如果应用频繁创建大量短生命周期对象(如字符串拼接、集合批量操作),可能导致频繁 Minor GC。
解决:使用 StringBuilder、对象池、批量操作。
2.2 TLAB 的配置
面试加分点:能说出"JDK 17+ 的 Epsilon GC 不回收任何对象(无操作 GC),适合短生命周期程序和性能测试,可以精确测量对象分配开销",说明他关注了 JDK 新特性。
面试陷阱:被问到"对象创建过程中,构造函数是什么时候被调用的",很多人会说"在分配内存之前"。准确答案是:在 new 指令分配内存后,在 <init> 方法执行时调用。字节码顺序是:new → dup → invokespecial <init>。