在日常开发和面试中,经常会被问到 “Java 中对象是如何被创建的?”
表面上只是一个 new
关键字,但 JVM 在幕后完成了一系列复杂操作。
可以总结为以下 六大步骤:
类加载检查 → 分配内存 → 内存清零 → 设置对象头 → 执行构造函数 → 引用赋值
一、类加载检查
当第一次创建某个类的对象时,JVM 会先检查这个类是否已经被加载:
如果未加载,则通过 类加载器(ClassLoader) 将
.class
文件读入内存,并在方法区中生成对应的类信息。如果已加载,则直接进入下一步。
这保证了类的元信息只会加载一次,所有对象实例共享同一份类描述信息。
二、分配内存
类信息已准备好后,JVM 会在 堆内存 中为新对象分配空间。
分配方式依赖于堆是否规整:
指针碰撞(Bump-the-pointer):堆内存连续,指针往后挪即可。
空闲列表(Free List):堆内存碎片化时,从空闲链表中找一块合适的内存。
这一步决定了对象的物理存储位置。
三、内存清零
为了避免读取到脏数据,JVM 会将新分配的内存空间全部清零:
数值型字段变为
0
布尔型字段变为
false
引用型字段变为
null
这是对象成员变量的默认初始化阶段。
四、设置对象头
JVM 会在新对象内存的起始位置写入 对象头 信息,包括:
Mark Word:保存哈希码、GC 年龄、锁状态等运行时数据
Klass Pointer:指向类的元信息,表明该对象属于哪个类
这是对象和 JVM 交互的桥梁,GC 和锁机制都依赖它。
五、执行构造函数
完成上述底层准备后,JVM 才会调用构造方法:
先执行父类构造函数,确保继承链上的属性都得到初始化
按照声明顺序给实例变量赋初始值
执行构造方法体中的逻辑
这一步才是程序员在代码中能感知到的初始化过程。
六、引用赋值
最后,新对象的引用会被返回,并赋值给栈帧中的局部变量表。
从这一刻开始,我们就可以通过引用来操作堆上的对象。