1. 程式人生 > 實用技巧 >hotspot虛擬機器物件

hotspot虛擬機器物件

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

一、建立物件

檢查->分配記憶體->初始化->設定

當虛擬機器遇到一條new指令去建立一個物件時,首先檢查指令的引數是否能在常量池中定位到一個類的符號引用,並檢查這個符號的引用代表的類是否已被載入解析、和初始化過。如果沒有則先執行相應類的載入過程。

在類載入檢查通過後,會為新物件分配記憶體。物件所需的記憶體在類載入後便可以完全確定。記憶體的分配方式有兩種,一種稱為指標碰撞(Bump the Pointer),一種稱為空閒列表(Free List)。指標碰撞是指記憶體在堆中是絕對規整的,而空閒列表則是已使用的記憶體和空閒的記憶體是相互交錯的,虛擬機器維護了一個列表記錄了哪些空閒的記憶體可用,分配的時候從列表中找尋一塊合適大小的空間進行劃分給物件例項。虛擬機器採用哪種分配方式是由java堆是否規整決定的,而java堆是否規整又是由垃圾收集器是否帶有壓縮整理功能決定。

採用指標碰撞帶Compact功能的收集器如:Serial、ParNew;

採用空閒列表的收集器如:CMS。

在分配記憶體的時候除了考慮到可用空間劃分的問題外,還涉及到一個需要考慮的問題,即物件建立時的併發問題。解決方案包括兩種:1.對分配記憶體空間的操作進行同步處理;2.把記憶體分配的動作按照執行緒劃分到不同的空間進行,即每個執行緒在java堆中預先分配一小塊記憶體,稱為本地執行緒分配緩衝(TLAB)。

記憶體分配完成後,需要將分配到的記憶體空間都初始化為零值,該操作保證了物件的例項欄位在Java程式碼中可以不賦值就直接使用。

再之後,虛擬機器會對物件進行一些必要的設定(包括物件是哪個類的例項、如何找到類的元資料資訊、物件的雜湊碼等)。

至此,虛擬機器認為物件已經產生了。但Java程式後續還會對物件進行init方法。

二、物件的記憶體佈局

物件在記憶體中的佈局可以分為3個部分:物件頭(Header)、例項資料(Instance Data)和對齊填充(Padding)。

物件頭包括兩部分資訊。一部分用於儲存物件自身的執行時資料(如雜湊碼、鎖狀態標誌、執行緒持有的鎖等等);另一部分是型別指標(即物件指向他的類元資料的指標,虛擬機器通過這個指標確定這個物件是哪個類的例項)。

例項資料存放的是物件真正儲存的有效資訊(包括父類繼承的和子類定義的)。各欄位的儲存順序會受到虛擬機器分配策略引數和欄位在java原始碼中定義順序的影響。

對齊填充並非是必然存在的。其意義在於因為HotSpot虛擬機器的自動填充要求物件的起始地址必須是8位元組的整數倍(即物件的大小必須是8位元組的整數倍),故需要通過對齊填充來補充。

三、物件的訪問定位

Java程式通過棧上的reference資料來操作堆上的物件例項。而reference型別只規定一個指向物件的引用,並沒定義該引用以何種方式定位訪問物件的具體位置,所以目前主流的物件訪問方式包括使用控制代碼訪問和直接指標兩種。

1.使用控制代碼訪問:Java堆中會劃分一塊記憶體作為控制代碼池,reference中儲存的就是物件的控制代碼地址,控制代碼中包含了物件例項資料和型別資料各自的具體地址資訊,如下:

191746_LUZR_2554810.png

2.使用指標訪問:reference中儲存的直接就是物件地址,而在堆物件中就要考慮如何放置訪問型別資料的資訊,如下:

192230_irbu_2554810.png

兩種方式的比較各有優勢。控制代碼的好處在於reference中儲存的是穩定的控制代碼地址資訊,在物件被移動時(當垃圾收集時,物件移動是非常普遍的行為)只會改變控制代碼中的例項資料指標,而reference本身無需修改;而直接指標的方式的好處也可以從圖中看出,相較於控制代碼而言,他節省了一次指標定位的時間開銷,速度更快。

轉載於:https://my.oschina.net/betteru/blog/1545014