Java面試知識點(五十一)虛擬機中的對象

HotSpot虛擬機是java虛擬機中主要使用的一種,我們平時所說的java虛擬機是泛指,而Hotspot虛擬機就是其中使用最廣泛的一種


對象的創建

Java 對象的創建過程
- 類加載檢查
- 分配內存
- 初始化零值
- 設置對象頭
- 執行 init 方法


  1. 類加載檢查
    虛擬機遇到一條 new 指令時,首先將去檢查這個指令的參數是否能在常量池中定位到這個類的符號引用,並且檢查這個符號引用代表的類是否已被加載過、解析和初始化過。如果沒有,那必須先執行相應的類加載過程。
    詳細請看Java面試知識點(三十六)類加載機制

  1. 分配內存
    在類加載檢查通過後,接下來虛擬機將爲新生對象分配內存。對象所需的內存大小在類加載完成後便可確定,爲對象分配空間的任務等同於把一塊確定大小的內存從 Java 堆中劃分出來。分配方式有 “指針碰撞” 和 “空閒列表” 兩種,選擇那種分配方式由 Java 堆是否規整決定,而 Java 堆是否規整又由所採用的垃圾收集器是否帶有壓縮整理功能決定。
    詳見Java面試知識點(五十二)虛擬機 分配內存

  1. 內存分配完成後,虛擬機需要將分配到的內存空間都初始化爲零值(不包括對象頭),這一步操作保證了對象的實例字段在 Java 代碼中可以不賦初始值就直接使用,程序能訪問到這些字段的數據類型所對應的零值。

  1. 設置對象頭
    初始化零值完成之後,虛擬機要對對象進行必要的設置,例如這個對象是哪個類的實例、如何才能找到類的元數據信息對象的哈希碼、對象的 GC 分代年齡等信息。 這些信息存放在對象頭中。 另外,根據虛擬機當前運行狀態的不同,如是否啓用偏向鎖等,對象頭會有不同的設置方式。

  1. 執行 init 方法
    在上面工作都完成之後,從虛擬機的視角來看,一個新的對象已經產生了,但從 Java 程序的視角來看,對象創建纔剛開始, 方法還沒有執行,所有的字段都還爲零。所以一般來說,執行 new 指令之後會接着執行 方法,把對象按照程序員的意願進行初始化(構造方法),這樣一個真正可用的對象纔算完全產生出來。

對象的內存佈局

在 Hotspot 虛擬機中,對象在內存中的佈局可以分爲 3 塊區域:對象頭、實例數據和對齊填充

Hotspot 虛擬機的對象頭包括兩部分信息,第一部分用於存儲對象自身的自身運行時數據(哈希碼、GC 分代年齡、鎖狀態標誌等等),另一部分是類型指針,即對象指向它的類元數據的指針,虛擬機通過這個指針來確定這個對象是哪個類的實例。

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

對齊填充部分不是必然存在的,也沒有什麼特別的含義,僅僅起佔位作用。 因爲 Hotspot 虛擬機的自動內存管理系統要求對象起始地址必須是 8 字節的整數倍,換句話說就是對象的大小必須是 8 字節的整數倍。而對象頭部分正好是 8 字節的倍數(1 倍或 2 倍),因此,當對象實例數據部分沒有對齊時,就需要通過對齊填充來補全。


對象的訪問定位

建立對象就是爲了使用對象,我們的 Java 程序通過棧上的 reference 數據來操作堆上的具體對象。對象的訪問方式有虛擬機實現而定,目前主流的訪問方式有①使用句柄和②直接指針兩種:

  • 句柄: 如果使用句柄的話,那麼 Java 堆中將會劃分出一塊內存來作爲句柄池,reference 中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址信息;
  • 直接指針: 如果使用直接指針訪問,那麼 Java 堆對象的佈局中就必須考慮如何放置訪問類型數據的相關信息,而 reference 中存儲的直接就是對象的地址。

這兩種對象訪問方式各有優勢。使用句柄來訪問的最大好處是 reference 中存儲的是穩定的句柄地址,在對象被移動時只會改變句柄中的實例數據指針,而 reference 本身不需要修改。使用直接指針訪問方式最大的好處就是速度快,它節省了一次指針定位的時間開銷。

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