深入理解JVM-hotspot虛擬機對象探秘


1.背景與大綱

  在我們了解了java虛擬機的運行時數據區后,我們大概知道了虛擬機內存的概況,但是我們還是不清楚具體怎么存放的訪問的;

  接下來,我們將深入探討HotSport虛擬機在java堆中對象的分配、布局、訪問的全過程。

2.對象創建

  

  1.類加載:當遇到new指令時,先判斷這個類是否被加載、解析、初始化過,如果沒有,先執行相應類的加載過程(后面會詳細分析這個過程)。

  2.分配內存:

  如果Java堆內存是規整連續的,采用“指針碰撞”的分配方式,

  

  如果是不連續規整的,采用“空閑列表”分配方式。如下圖:灰色表示已使用,數字表示可用

  

  內存是否規整取決於垃圾收集器是否帶有壓縮整理功能

  Serial,ParNew等帶有Compact過程的收集器,采用的分配算法是“指針碰撞”。

  而CMS這種基於Mark-Sweep算法的收集器,通常采用“空閑列表”分配方式。

 

  線程安全問題:即便是修改指針指向位置,A\B兩個線程有可能會指向同一個地址

  

  解決方案:
  a.同步鎖:
  b.TLAB:本地線程分配緩沖,把內存的分配動作按照線程划分在不同的空間進行
  等TLAB用完分配新的TLAB時,才需要同步鎖
  虛擬機是否使用TLAB,可以通過-XX:+/UseTLAB參數設定

  

  3.對象初始化為零對象:

  a.如果使用的是TLAB,這一步可以提前到TLAB分配的時候進行
  b.作用:保證了實例字段在java代碼中可以不賦初始值就可以直接使用

  4.對象頭進行設置,
  包括這個對象是哪個類的實例、如何才能找到類的元數據信息、對象的哈希碼、對象的GC分代年齡等信息。

  5.java程序初始化,最后執行init方法,把對象按照程序員的意願進行初始化。這樣一個真正可用的對象才算完全生產出來。

3.對象內存布局  

  

  對象內存布局分為三塊區域

3.1.對象頭(Header)

  對象頭主要包括:運行時數據、類型指針

3.1.1.運行時數據

  對象頭,存儲對象自身的運行時數據,如哈希碼、對象的GC分代年齡、鎖狀態標志、偏向線程ID、偏向時間戳,
  1.這部分數據的長度在32位和64位虛擬機中,分別為32bit和64bit。
  2.官方稱其為:Mark Word
  3.存儲的運行時數據過多(超出32bit、64bit)
  4.Mark Word被設計成為一個非固定數據結構,以便在極小的空間內存存儲盡量多的信息

  

  5.根據對象的狀態復用自己的存儲空間

 3.1.2.類型指針

  另一個部分是類型指針
  1.定義:對象指向它的類元數據的指針
  2.作用:虛擬機通過這個指針來確定這個對象是哪個類的實例
  3.但是:並不是所有的虛擬機實現都必須在對象上保留類型指針
  4.結論:查找對象的元數據信息並不一定要經過對象本身(下一節詳細講)

3.1.3.特例

  另外,如果對象是java數組
  1.對象頭必須有一塊用於記錄數組長度的數據
  2.原因:普通java對象的元數據信息可以確定java對象的大小
  3.但是,從數組的元數據中卻無法確定數組的大小

3.2.實例數據(Instance Data)

  1.存儲內容:是對象真正存儲的有效信息,也是在程序代碼中所定義的各種類型的字段內容
  2.存儲范圍:無論是父類還是子類都要記錄
  3.存儲順序:存儲順序會受到虛擬機分配策略參數(fieldsAllocationStyle)和字段定義順序的影響
  4.默認分配策略順序:按字節由大到小(即由寬到窄),
  longs/doubles->ints->shorts/chars->bytes/booleans->oops(Ordinary Object Pointers)普通對象指針
  5.在滿足分配策略這個前提條件下,父類中定義的變量會出現在子類之前
  6.如果CompactFields參數值為true,那么子類中較窄的變量也可能會插入到父類變量的空隙之中

3.3.對齊補充(Padding)

  1.作用:對齊填充並不是必然存在的,也沒有特別的含義,它僅僅起着占位符的作用。
  2.原因:由於HotSpot VM的自動內存管理系統要求對象起始地址必須是8字節的整數倍,換句話說,就是對象的大小必須是8字節的整數倍。
  3.解決:而對象頭部分正好是8字節的倍數(1倍或者2倍),因此,當對象實例數據部分沒有對齊時,就需要通過對齊填充來補全。

4.對象的訪問定位

  

  對象的訪問定位
  1.Java程序需要通過棧上的reference數據來操作堆上的具體對象。
  2.由於reference類型在Java虛擬機規范中只規定了一個指向對象的引用,
  3.並沒有定義這個引用應該通過何種方式去定位、訪問堆中的對象的具體位置,
  4.所以對象訪問方式也是取決於虛擬機實現而定的。
  5.目前主流的訪問方式有使用句柄直接指針兩種。
  6.總結:通過reference數據來定位具體對象,但reference只規定了對象的引用,並沒有提供具體實現

  

4.1.句柄方式

  1.Java堆中將會划分出一塊內存來作為句柄池,
  2.reference中存儲的就是對象的句柄地址,
  3.而句柄中包含了對象實例數據類型數據各自的具體地址信息。

  優點:
  使用句柄來訪問的最大好處就是reference中存儲的是穩定的句柄地址,

  在對象被移動(垃圾收集時移動對象是非常普遍的行為)時只會改變句柄中的實例數據指針,而reference本身不需要修改

4.2.直接指針方式

  1.Java堆對象的布局中就必須考慮如何放置訪問類型數據的相關信息,
  2.而reference中存儲的直接就是對象地址。

  優點:
  使用直接指針訪問方式的最大好處就是速度更快,它節省了一次指針定位的時間開銷

  由於對象的訪問在Java中非常頻繁,因此這類開銷積少成多后也是一項非常可觀的執行成本。

4.3.案例

  虛擬機Sun HotSpot 使用的是直接指針的方式

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM