對象的分配、佈局、訪問過程

整理自《深入理解 Java 虛擬機》。

1. 對象的創建

虛擬機遇到一條 new 指令時,將執行以下過程:

  1. 檢查這個指令的參數是否能在常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已經被加載、解析和初始化過。如果沒有,必須先執行相應的類加載過程。
  2. 分配內存。對象所需內存的大小在類加載完成後便可完全確定,爲對象分配空間的任務等同於把一塊確定大小的內存從 Java 堆中劃分出來。根據採用的不同的垃圾收集器,採取的分配方式爲“指針碰撞”或“空閒列表”。
  3. 將分配的內存空間都初始化爲零值(不包括對象頭)。
  4. 對對象頭進行設置。包括這個對象是哪個類的實例、如何才能找到類的元數據信息、對象的哈希碼、對象的 GC 分代年齡等信息。
  5. 執行 < init > 方法,把對象按照程序員的意願進行初始化。

2. 對象的內存佈局

在這裏插入圖片描述
對象頭

markWord:用幹存儲對象自身的運行時數據,如哈希碼 (HashCode) 、 GC 分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程 ID 、偏向時間戳等。

類元數據指針:對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。

實例數據部分:對象真正存儲的有效信息,也是在程序代碼中所定義的各種類型的字段內容。

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

3. 對象的訪問定位

Java 程序需要通過棧上的 reference 數據來操作堆上的具體對象。兩種方式:

使用句柄訪問
Java 堆中劃分出一塊內存作爲句柄池,reference 中存儲的就是對象的句柄地址,句柄中包含了對象實例數據與類型數據各自的具體地址信息。
在這裏插入圖片描述
使用句柄的好處是 reference 中儲存的是穩定的句柄地址,在對象被移動(垃圾收集時移動對象是非常普遍的行爲)時只會改變句柄中的實例數據指針,而 reference 本身不需要修改。

使用直接指針訪問
Java堆對象的佈局必須考慮如何放置訪問類型數據的相關信息,而 reference 中儲存的直接就是對象地址。
在這裏插入圖片描述
使用直接指針的最大好處就是速度更快,它節省了一次指針定位的時間開銷,由於對象的訪問在 Java 中非常頻繁,因此這類開銷積少成多後也是一項非常可觀的執行成本。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章