對象的創建、對象的內存佈局、對象訪問定位

對象實例化的幾種方式

在這裏插入圖片描述

創建對象的步驟

在這裏插入圖片描述

  1. 虛擬機遇到一.條new指令,首先去檢查這個指令的參數能否在Metaspare(元空間)的常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已經被加載、解析和初始化。( 即判斷類元信息是否存在)。如果沒有,那麼在雙親委派模式下,使用當前類加載器以ClassLoader+包名+類名爲Key進行查找對應的.class文件。如果沒有找到文件,則拋出ClassNotFoundException異常,如果找到,則進行類加載(加載、解析和初始化),並生成對應的Class類對象

  2. 首先計算對象佔用空間大小,接着在堆中劃分一塊內存給新對象。如果實例成員變量是引用變量,僅分配引用變量空間即可,即4個字節大小。(選擇哪種分配方式由Java堆是否規整決定,而Java堆是否規整又由所採用的垃圾收集器是否帶有壓縮整理功能決定。)
    如果內存是規整的,那麼虛擬機將採用的是指針碰潼法( Bump The Pointer )來爲對象分配內存。意思是所有用過的內存在一邊,空閒的內存在另外一邊,中間放着一個指針作爲分界點的指示器,分配內存就僅僅是把指針向空閒那邊挪動一段與對象大小相等的距離罷了。如果垃圾收集器選擇的是Serial、ParNew這種基於壓縮算法的,虛擬機採用這種分配方式。一般使用帶有compact (整理)過程的收集器時,使用指針碰撞。
    如果內存不是規整的,已使用的內存和未使用的內存相互交錯,那麼虛擬機將採用的是空閒列表法來爲對象分配內存。意思是虛擬機維護了一個列表,記錄上哪些內存塊是可用的,再分配的時候從列表中找到一塊足夠大的空間劃分給對象實例,並更新列表上的內容。這種分配方式成爲“空閒列表( Free List ) "。

  3. 處理併發安全問題:在分配內存空間時,另外一個問題是及時保證new對象時候的線程安全性:創建對象是非常頻繁的操作,虛擬機需要解決併發問題。虛擬機採用 了兩種方式解決併發問題:
    ① CAS ( Compare And Swap ) 失敗重試、區域加鎖:保證指針更新操作的原子性;② TLAB把內存分配的動作按照線程劃分在不同的空間之中進行,即每個線程在Java堆中預先分配一小塊內存,稱爲本地線程分配緩衝區(TLAB ,Thread Local Allocation Buffer) 虛擬機是否使用TLAB,可以通過-XX : + / - UseTLAB參數來設定。

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

  5. 設置對象頭:將對象的所屬類(即類的元數據信息)、對象的HashCode和對象的GC信息、 鎖信息等數據存儲在對象的對象頭中。這個過程的具體設置方式取決於JVM實現。

  6. 顯示初始化:在Java程序的視角看來,初始化才正式開始。初始化成員變量,執行實例化代碼塊。調用類的構造方法,並把堆內對象的首地址賦值給引用變量。因此一般來說(由字節碼中是否跟隨有invokespecial指令所決定), new指令之後會接着就是執行方法,把對象按照程序員的意願進行初始化,這樣一個真正可用的對象纔算完全創建出來。

對象的內存佈局

在這裏插入圖片描述
在這裏插入圖片描述

對象的訪問定位

JVM是如何通過棧幀中的對象引用訪問到其內部的對象實例的呢?
在這裏插入圖片描述
對象訪問方式有兩種:

  1. 句柄訪問:優點:reference中存儲穩定句柄地址,對象被移動(垃圾收集時移動對象很普遍)時只會改變句柄中實例數據指針即可, reference本身不需要被修改。缺點:在堆空間中單獨開闢了一個句柄池空間,而且這樣的訪問效率比較低。

在這裏插入圖片描述
3. 直接指針(hotspot採用)在這裏插入圖片描述

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