1. 程式人生 > 實用技巧 >5、帶著問題學習JVM_HotSpot物件的建立、記憶體佈局、物件訪問

5、帶著問題學習JVM_HotSpot物件的建立、記憶體佈局、物件訪問

   瞭解了Java虛擬機器記憶體模型的概況,會更進一步想了解這些虛擬機器記憶體中資料的其他細節,譬如它們是如何建立、如何佈局以及如何訪問的。對於這樣涉及細節的問題,必須把討論範圍限定在具體的虛擬機器和集中在某一個記憶體區域上才有意義。建立物件通常(例外:複製、反序列化)僅僅是一個new關鍵字而已,而在虛擬機器中,普通java物件(不包括陣列和Class物件等)的建立又是怎樣一個過程呢?下面以最常用的虛擬機器HotSpot和最常用的記憶體區域Java堆為例,一起學習HotSpot虛擬機器在Java堆中物件分配、佈局和訪問的全過程。
1、普通JAVA物件的建立
步驟1:類載入
當Java虛擬機器遇到一條位元組碼new指令時,首先將去檢查這個指令的引數是否能在常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已被載入、解析和初始化過。如果沒有,那必須先執行相應的類載入過程。
步驟2:為新生物件分配記憶體,物件所需記憶體的大小在類載入完成後便可完全確定。根據java堆是否規整,堆記憶體分配有兩種方式:
a.“指標碰撞”(Bump The Pointer):假設Java堆中記憶體是絕對規整的,所有被使用過的記憶體都被放在一邊,空閒的記憶體被放在另一邊,中間放著一個指標作為分界點的指示器,那所分配記憶體就僅僅是把那個指標向空閒空間方向挪動一段與物件大小相等的距離。
b.“空閒列表”(Free List):如果Java堆中的記憶體並不是規整的,已被使用的記憶體和空閒的記憶體相互交錯在一起,那就沒有辦法簡單地進行指標碰撞了,虛擬機器就必須維護一個列表,記錄上哪些記憶體塊是可用的,在分配的時候從列表中找到一塊足夠大的空間劃分給物件例項,並更新列表上的記錄。

問題:新物件記憶體分配方式該如何選擇?
選擇哪種分配方式由Java堆是否規整決定,而Java堆是否規整又由所採用的垃圾收集器是否帶有空間壓縮整理(Compact)的能力決定。Serial、ParNew等帶壓縮整理過程的收集器,系統採用的分配演算法是指標碰撞,既簡單又高效;CMS這種基於清除(Sweep)演算法的收集器時,理論上 就只能採用較為複雜的空閒列表來分配記憶體。在CMS的實現裡面,為了能在多數情況下分配得更快,設計了一個叫作Linear Allocation Buffer的分配緩衝區,通過空閒列表拿到一大塊分配緩衝區之後,在它裡面仍然可以使用指標碰撞方式來分配。

2、物件的記憶體佈局

物件在堆記憶體中的儲存佈局可以劃分為以下三個部分
2.1 物件頭(Header):包含以下兩部分資料
a.其中一部分用於儲存物件自身的執行時資料,如雜湊碼(HashCode)、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等。這部分資料官方稱為“Mark Word”
b.另一部分是型別指標,即物件指向它的型別元資料的指標,Java虛擬機器通過這個指標來確定該物件是哪個類的例項。

2.2 例項資料(Instance Data):
是物件真正儲存的有效資訊,即我們在程式程式碼裡面所定義的各種型別的欄位內容,無論是從父類繼承下來的,還是在子類中定義的欄位。
這部分的儲存順序會受到虛擬機器分配策略引數(-XX:FieldsAllocationStyle引數)和欄位在Java原始碼中定義順序的影響。相同寬度的欄位總是被分配到一起存放,在父類中定義的變數會出現在子類之前。子類之中較窄的變數也允許插入父類變數的空隙之中,以節省出一點點空間(通過+XX:CompactFields引數值為true控制,這也是HotSpot虛擬機器的預設設定)。
備註:Allocation 分配、compact 小型的 緊密的
2.3 對齊填充(Padding):
非必然存在,也沒有特別的含義,僅起著佔位符的作用。HotSpot虛擬機器的自動記憶體管理系統要求任何物件的大小都必須是8位元組的整數倍。物件頭部分已經被精心設計成正好是8位元組的倍數(1倍或者2倍),因此,如果物件例項資料部分沒有對齊的話,就需要通過對齊填充來補全。

3、物件的訪問定位

Java程式會通過棧上的reference資料來操作堆上的具體物件。reference型別在《Java虛擬機器規範》裡面只規定了它是一個指向物件的引用,並沒有定義這個引用應該通過什麼方式去定位、訪問到堆中物件的具體位置,所以物件訪問方式也是由虛擬機器實現而定的,主流的訪問方式主要有使用控制代碼和直接指標兩種:
a.使用控制代碼:Java堆中將可能會劃分出一塊記憶體來作為控制代碼池,reference中儲存的就是物件的控制代碼地址,而控制代碼中包含了物件例項資料與型別資料各自具體的地址資訊。
b.使用直接指標:Java堆中物件的記憶體佈局就必須考慮如何放置訪問型別資料的相關資訊,reference中儲存的直接就是物件地址,如果只是訪問物件本身的話就不需要多一次間接訪問的開銷。其好處是速度更快。
備註:HotSpot虛擬機器主要就是使用控制代碼這種方式
學習書籍:深入理解Java虛擬機器:JVM高階特性與最佳實踐第3版.pdf