一、創建對象的完整過程
?1. 類加載檢查
JVM 遇到
new
指令時,首先去檢查這個類User
是否已經被加載、解析和初始化過。如果沒有,就先執行 類加載過程(加載
.class
文件到方法區/元空間、創建 Class 對象等)。【這個過程就是加載、驗證、準備、解析、初始化】
?2. 分配內存
JVM 在 堆(Heap) 中為新對象分配內存。
分配方式可能是:
指針碰撞(bump-the-pointer):堆內存規整時,用一個指針往后挪就能完成分配。
空閑列表(free-list):堆內存不規整時,需要維護可用內存塊列表來找到一塊合適的空間。
?3. 內存初始化(零值初始化)
對分配到的內存空間進行 零值初始化,保證對象的實例字段有默認值。
比如:
int
→0
,Object
→null
,boolean
→false
。這樣保證 Java 程序不讀到臟數據。
?4.??設置對象頭
JVM 會在對象頭中寫入信息:
哈希碼(可能延遲計算)
GC 分代年齡
類的元數據指針(指向方法區中的 Class 對象)
鎖信息(偏向鎖/輕量級鎖/重量級鎖)
?5. 執行
<init>
方法(構造函數)
new
指令會執行對象的構造函數,真正完成對象的初始化。先執行父類構造方法,再執行子類構造方法。
至此,字段會被賦予程序員寫的初始化值(而不是零值)。
?6. 返回對象引用
new
指令執行完畢后,返回對象在堆中的引用地址。
user
變量存放的是這個對象的 引用(指針),而不是對象本身。
二、簡化流程圖
new -> 類加載檢查 -> 分配內存 -> 零值初始化 -> 設置對象頭 -> 調用構造函數 <init> -> 返回引用
三、額外說明
棧 vs 堆
對象本身存放在堆里;
引用變量(比如
user
)在棧幀的局部變量表里,存放堆中對象的地址。優化:TLAB(Thread Local Allocation Buffer)
多線程環境下,為了避免內存分配加鎖,JVM 會給每個線程分配一小塊內存(TLAB)。
絕大多數對象都能在 TLAB 上直接分配,效率非常高。
逃逸分析 & 棧上分配
JIT 優化時,如果發現對象不會逃出方法范圍,可以直接在 棧 上分配,而不是堆。
這樣方法執行完對象就回收,不需要 GC。
? 總結一句話:
Java 創建對象的過程是:
類加載檢查 → 堆內存分配 → 零值初始化 → 設置對象頭 → 調用構造函數 → 返回引用。