1. 程式人生 > 其它 >深入理解JVM - HotSpot 虛擬機器物件

深入理解JVM - HotSpot 虛擬機器物件

1. HotSpot 虛擬機器物件

如何建立、如何佈局、如何訪問。

2. 物件建立

Class載入 --> 記憶體分配 --> 記憶體初始化 --> 物件初始化.

2. 類載入

當VM遇到位元組碼 new 指令,檢查這個指令的引數在常量池能否定位到一個類的符號引用,並檢查這個符號引用代表的類是否已被載入、解析和初始化過,沒有則需要先執行對應的類載入過程。

3. 記憶體分配

類載入檢查通過後,則為新生物件分配記憶體,物件所需大小在類載入後便可完全確定。
指標碰撞(Bump The Pointer)
Java堆記憶體絕對規整,已使用過的記憶體放在一邊,未使用過的記憶體在一邊,中間使用指標作為指示器,則記憶體分配僅需要移動指標。

空閒連結串列(Free List)
java堆記憶體不規整,需要使用連結串列維護哪些記憶體是空閒的,哪些已使用過,分配時從空閒連結串列找到足夠大記憶體劃分物件例項,並更新連結串列。

選擇哪種方式 ---> Java堆是否規整 ---> 所採用的的垃圾收集器是否帶有空間壓縮整理(Compact)。
Serial、ParNew 等帶有空間壓縮整理,使用指標碰撞,簡單高效。
CMS 採用清除演算法(Sweep)實現,使用空閒連結串列。

實際上即便不規整也會兩種均使用,空閒連結串列管理大塊記憶體,然後在大塊記憶體中採用指標碰撞分配。
CMS 實現中使用了 Linear Allocation Buffer 的分配緩衝器區,先通過空閒連結串列獲取大塊分配緩衝區,在緩衝區中使用指標碰撞分配。

記憶體分配的執行緒安全
一種是 CAS + 失敗重試進行分配。
另一種是每個執行緒預先在 Java 堆中分配執行緒私有的分配緩衝區,Thread Local Allocation Buffer,TLAB,執行緒分配時先在TLAB中分配,沒有足夠記憶體再使用 CAS + 失敗重試從堆中分配 TLAB。

-XX:+/-UseTLAB 啟用或關閉 TLAB

4. 記憶體初始化

這好像也是 Java 記憶體模型的一部分,主要是保證欄位即便未顯式初始化也可直接使用。
可提前到 TLAB 分配時期就處理。
把記憶體清零即可。

5. 物件初始化

物件頭的初始化,如是哪個類的例項、如何找到類的元資料資訊、物件雜湊碼(Object#hashCode呼叫時才計算)、GC分代年齡等。
建構函式呼叫 <init>()

,由 new 指令後是否有 invokespecial 指令決定,java編譯器會在new指令後同時生成invokespecial,通過其他方式產生的則不一定。

3. 物件記憶體佈局

三部分:物件頭(Header)、例項資料(InstanceData)、對齊填充(Padding)。

1. 物件頭

兩部分資訊
- 物件自身執行時資料 Mark Word:雜湊碼、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等。這部分在32位和64位VM(未開啟指標壓縮)中分別32位和64位。
- 型別指標:例項的型別元資料指標,VM通過這個指標確定該物件是哪個類的例項。不一定所有實現都需要在物件頭保留型別指標。
- 陣列長度:如果例項是一個數組的話

Mark Word
動態資料結構,由於執行時資料較多,不同情況下儲存的資料不一樣。

例項資料
所有欄位內容,無論父子類的都需要記錄下來(因為最終是一個例項),順序受VM分配策略(-XX:FieldsAllocationStyle)和原始碼定義順序影響。
預設:longs/doubles、ints、shorts/chars、bytes/booleans、oops(Ordinary Object Pointers,OOPs)。
預設相同寬度欄位一起,這個前提下,父類欄位在子類欄位前。
若 HotSpot 虛擬機器的+XX:CompactFields引數值為true(預設),那子類之中較窄的變數也允許插入父類變數的空隙之中,以節省出一點點空間。

對其填充
非必然存在,佔位符作用,主要是 HotSpot 的自動記憶體管理系統要求物件起始地址必須是8位元組的整數倍,也即要求任何物件的大小都必須是8位元組的整數倍。
物件頭部分已經被精心設計成正好是8位元組的倍數(1倍或者2倍)。

4. 物件訪問定位

常用方法:控制代碼、直接指標。
控制代碼
堆記憶體劃出控制代碼池,reference 儲存的是物件的控制代碼地址,控制代碼中包含了物件例項資料和型別資料各自地址資訊。
reference 儲存穩定控制代碼地址,若物件移動,僅需要修改控制代碼池中的資料。

直接指標
reference 儲存的直接是物件例項的地址,物件頭需要使用型別指標儲存物件所屬型別的資訊。
若只是訪問例項,則與控制代碼相比少了一次間接訪問的開銷。

Hotspot 使用直接指標的訪問方法。

5. 擴充套件

  1. 啟動JVM至少有哪些執行緒? new 的過程哪些是執行緒安全的?是使用者呼叫執行緒執行的嗎
  2. 物件大小計算