對象創建的過程


https://mp.weixin.qq.com/s/y82t0a4dTBZwgY6MRnZDIw

創建對象的方式有4種:new 關鍵字、反射機制、Object 類的 clone 方法、反序列化。

針對 new 關鍵字的方式,來談談對象創建的過程,例如 Demo 類:

// 創建Demo類的實例對象
Demo demo = new Demo();
// 定義Demo類
public class Demo {
    private int i;
    private String str = "初始值";

    // 構造代碼塊
    {
        /* do something */
    }

    // 靜態代碼塊
    static {
        /* do something */
    }

    // 構造方法
    public Demo() {

    }

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }
}

創建對象的過程一共5個步驟,如下所示:

 

一、檢查類是否已經被加載

當 JVM 執行到 字節碼 new 指令時,首先會到 常量池 中定位到 Demo 類的符號引用,通過符號引用檢查 Demo 類是否已被加載。

如果 Demo 類沒有被被加載,就必須先執行加載過程。

二、為對象分配內存空間

Demo 類的加載檢查通過后,JVM 將會從堆中為 new Demo() 創建的實例對象分配確定大小的內存。

分配內存的方式有兩種:指針碰撞、空閑列表

  • 指針碰撞

垃圾回收算法是標記-壓縮算法,堆內存的空間很整齊的,沒有碎片空間,使用中的內存放在一端,空閑的內存在另一端,在中間放置一個指針作為分界點。

分配內存時,只需要將指針往空閑內存的那一端挪動一段與對象大小的距離。

  • 空閑列表

垃圾回收算法是標記-清除算法,堆內存的空間很凌亂的,存在碎片空間,使用的內存和空閑的內存相互交錯的在一起。

這種情況下,JVM 需要維護一個空閑列表,記錄哪些是空閑內存,分配內存的時候從空閑列表中找到一塊足夠大的內存空間存放對象,並更新空閑列表

 

三、將分配到的內存空間初始化零值(不包括對象頭)

為對象的字段進行初始化默認值,保證對象即使沒有賦初值也可以直接使用。

 int  初始化為 0,boolean初始化為false,string 初始化為 null。

 

四、為對象進行必要的設置

設置對象頭(Object Head),包括這個對象所屬的類,類的元數據信息,對象的哈希碼,對象的 GC 分代年齡等信息。

 

五、執行構造方法

按照順序執行:

  • 執行 Class 文件中的 <init>() 方法

  • 初始化對象的字段:例如 str 初始化為 "初始值" 

  • 靜態代碼塊

  • 構造代碼塊

  • 構造方法

對象在堆內存中的存儲布局划分為三個部分:對象頭、實例數據、對齊填充。

對象頭包括兩部分信息:對象自身的運行時數據、類型指針。


 

一、對象頭

對象頭包括兩部分信息:對象自身的運行時數據、類型指針。

  • 對象自身的運行時數據(Mark Word)

這些數據包括:哈希碼、GC 分代年齡、鎖狀態標志、線程持有鎖、偏向線程ID、偏向時間戳等。

這部分數據的長度在32位和64位的 JVM 中分別為:32 bit、64 bit。

  • 類型指針

就是對象指向它的類型元數據的指針,可以理解為 demo 指向 Demo.class。

 

二、實例數據

實例數據就是對象中的所有字段內容,包括從父類繼承的所有字段內容。

例如 demo 中的 i 和 str 。

 

三、對齊填充

對齊填充並不是必然存在的,也沒有特別的含義,它僅僅是起到占位符的作用。

因為 JVM 存儲對象的內存大小必須是8字節的整數倍。

那么,當對象的大小不是8字節的整數倍,意味着對象申請到的內存大小,必然大於對象的大小,剩余的內存空間就是對齊填充。

對象的訪問定位

Demo demo = new Demo();

這一行代碼,demo 只是對象的引用,並不是真正創建的對象,demo 在內存中存儲的是 new Demo() 實例對象的地址。

demo  存儲在棧,new Demo() 所創建的實例對象存儲在堆。

demo 訪問 new Demo() 所創建的實例對象有兩種方式:句柄、直接指針。

 

一、句柄

句柄池存在堆,那么 demo 存儲的就是 new Demo() 的句柄地址,句柄中包含了對象的實例數據和類型數據的實際地址。

以 Demo 類為例,如下圖所示:

 

 

 

二、直接指針

直接指針 就是 demo 存儲的就是 new Demo() 的實際地址。

以 Demo 類為例,如下圖所示:

 

句柄 相比於 直接訪問 需要多一次間接訪問的開銷,不過句柄訪問最大的好處就是靈活,因為引用存儲的是句柄地址,句柄地址是不會發生改變的。

這相當於多了一層抽象,句柄就是這一層抽象。

這意味着,即使對象的實際地址發生改變,只需要修改句柄中的實例數據指針的地址,不需要修改引用的值,即 demo 存儲的值。

垃圾回收在使用 標記-壓縮算法 的情況下,對象的實際地址發生改變是十分普遍的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM