深入理解JVM(二)——揭開HotSpot物件建立的奧祕

喝水會長肉發表於2021-12-30

物件的建立過程

當虛擬機器遇到一條含有new的指令時,會進行一系列物件建立的操作:

檢查常量池中是否有即將要建立的這個物件所屬的類的符號引用;

若常量池中沒有這個類的符號引用,說明這個類還沒有被定義!丟擲ClassNotFoundException;

若常量池中有這個類的符號引用,則進行下一步工作;

進而檢查這個符號引用所代表的類是否已經被JVM載入;

若該類還沒有被載入,就找該類的class檔案,並載入進方法區;

若該類已經被JVM載入,則準備為物件分配記憶體;

根據方法區中該類的資訊確定該類所需的記憶體大小;

一個物件所需的記憶體大小是在這個物件所屬類被定義完就能確定的!且一個類所生產的所有物件的記憶體大小是一樣的!JVM在一個類被載入進方法區的時候就知道該類生產的每一個物件所需要的記憶體大小。

從堆中劃分一塊對應大小的記憶體空間給新的物件;

分配堆中記憶體有兩種方式:

指標碰撞

如果JVM的垃圾收集器採用複製演算法或標記-整理演算法,那麼堆中空閒記憶體是完整的區域,並且空閒記憶體和已使用記憶體之間由一個指標標記。那麼當為一個物件分配記憶體時,只需移動指標即可。因此,這種在完整空閒區域上通過移動指標來分配記憶體的方式就叫做“指標碰撞”。


空閒列表

如果JVM的垃圾收集器採用標記-清除演算法,那麼堆中空閒區域和已使用區域交錯,因此需要用一張“空閒列表”來記錄堆中哪些區域是空閒區域,從而在建立物件的時候根據這張“空閒列表”找到空閒區域,並分配記憶體。

綜上所述:JVM究竟採用哪種記憶體分配-方法,取決於它使用了何種垃圾收集器。

為物件中的成員變數賦上初始值(預設初始化);

設定物件頭中的資訊;

呼叫物件的建構函式進行初始化

此時,整個物件的建立過程就完成了。


物件的記憶體模型

一個物件從邏輯角度看,它由成員變數和成員函式構成,從物理角度來看,物件是儲存在堆中的一串二進位制數,這串二進位制數的組織結構如下。

物件在記憶體中分為三個部分:

物件頭

例項資料

對齊補充

1. 物件頭

物件頭中記錄了物件在執行過程中所需要使用的一些資料:雜湊碼、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等。

此外,物件頭中可能還包含型別指標。通過該指標能確定這個物件所屬哪個類。

此外,如果物件是一個陣列,那麼物件頭中還要包含陣列長度。

2. 例項資料

實力資料部分就是成員變數的值,其中包含父類的成員變數和本類的成員變數。

3. 對齊補充

用於確保物件的總長度為8位元組的整數倍。

HotSpot要求物件的總長度必須是8位元組的整數倍。由於物件頭一定是8位元組的整數倍,但例項資料部分的長度是任意的,因此需要對齊補充欄位確保整個物件的總長度為8的整數倍。


訪問物件的過程

我們知道,引用型別的變數中存放的是一個地址,那麼根據地址型別的不同,物件有不同的訪問方式:

控制程式碼訪問方式

堆中需要有一塊叫做“控制程式碼池”的記憶體空間,用於存放所有物件的地址和所有物件所屬類的類資訊。

引用型別的變數存放的是該物件在控制程式碼池中的地址。

 //java學習交流:603835449  進入可領取學習資源及對十年開發經驗大佬提問 免費解答! 

訪問物件時,首先需要通過引用型別的變數找到該物件的控制程式碼,然後根據控制程式碼中物件的地址再訪問物件。

直接指標訪問方式

引用型別的變數直接存放物件的地址,從而不需要控制程式碼池,通過引用能夠直接訪問物件。

但物件所在的記憶體空間中需要額外的策略儲存物件所屬的類資訊的地址。

比較

HotSpot採用直接指標方式訪問物件,因為它只需一次定址操作,從而效能比控制程式碼訪問方式快一倍。但它需要額外的策略儲存物件在方法區中類資訊的地址。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70010294/viewspace-2850112/,如需轉載,請註明出處,否則將追究法律責任。

相關文章