1. 程式人生 > >JVM系列2:HotSpot虛擬機器物件

JVM系列2:HotSpot虛擬機器物件

1.物件建立過程:

 

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

②.分配記憶體:類載入檢查完成後,虛擬機器將為新物件分配記憶體空間,且物件所需記憶體空間大小在其完成類載入檢查後即可確定,該過程其實就是在堆中劃分一小部分的確定大小的空間,用於儲存物件資訊。其中分配方式有以下兩種:

  記憶體分配的併發問題:

  在建立物件時存線上程安全問題,虛擬機器採用兩種方式來保證建立物件的執行緒安全:

  • CAS鎖+失敗重試:CAS是樂觀鎖的一種實現形式,虛擬機器採用CAS+失敗重試來保證更新操作的原子性。
  • TLAB:為每一個執行緒預先在Eden區分配一塊兒記憶體,JVM在給執行緒中的物件分配記憶體時,首先在TLAB分配,當物件大於TLAB中的剩餘記憶體或TLAB的記憶體已用盡時,再採用上述的CAS進行記憶體分配。

  樂觀鎖:樂觀鎖就是,每次不加鎖而是假設沒有衝突而去完成某項操作,如果因為衝突失敗就重試,直到成功為止

③.初始化零值:記憶體分配完畢後,虛擬機器將該物件分配得到的記憶體空間全部設定初始值零(不包含物件頭部分),該操作可以保證物件的例項欄位在程式碼中即使不賦予初始值就可以直接使用。程式能訪問到這些欄位的資料型別所對應的零值。

④.設定物件頭:初始化零值完成後,虛擬機器將物件的一些必要資訊存放在物件頭中,這些資訊包括:例如這個物件是哪個類的例項、如何才能找到類的元資料資訊、物件的雜湊嗎、物件的 GC 分代年齡等資訊。另外,根據虛擬機器當前執行狀態的不同,如是否啟用偏向鎖等,物件頭會有不同的設定方式。

⑤.執行Init方法:完成上述操作後,java虛擬機器即完成了一個物件的建立,但是對於java程式而言,對於該物件的一些定製的內容還未進行,<init>方法中包含了程式設計師的定製需求和意願,執行完init方法後,物件完成了初始化,此時才是一個可用物件。

2.物件的記憶體佈局

  虛擬機器中,物件在記憶體中的儲存包括三個部分:物件頭、例項資料和對齊填充。

Hotspot虛擬機器的物件頭:包括兩部分資訊,第一部分用於儲存物件自身的自身執行時資料(雜湊嗎、GC分代年齡、鎖狀態標誌等等),另一部分是型別指標,即物件指向它的類元資料的指標,虛擬機器通過這個指標來確定這個物件是那個類的例項。

例項資料部分:是物件真正儲存的有效資訊,也是在程式中所定義的各種型別的欄位內容。

對齊填充部分:不是必然存在的,也沒有什麼特別的含義,僅僅起佔位作用。 因為Hotspot虛擬機器的自動記憶體管理系統要求物件起始地址必須是8位元組的整數倍,換句話說就是物件的大小必須是8位元組的整數倍。而物件頭部分正好是8位元組的倍數(1倍或2倍),因此,當物件例項資料部分沒有對齊時,就需要通過對齊填充來補全。

3.物件的訪問定位

  Java程式通過java棧上的 reference 資料來操作堆上的具體物件。物件的訪問方式有虛擬機器實現而定,目前主流的訪問方式有①使用控制代碼和②直接指標兩種:

①.使用控制代碼:使用控制代碼訪問會在java堆中開闢一片區域作為控制代碼池,棧中的reference中儲存的就是物件的控制代碼地址,而控制代碼中則包含了物件例項資料和型別資料的地址資訊。

  

   在java棧中的reference中,包含了需要引用的物件的控制代碼地址,然後通過控制代碼地址在控制代碼池中找到指向物件例項資料和物件型別資料的指標,從而實現引用該物件。

    優點:引用中儲存的是控制代碼地址,在物件被移動(垃圾回收時物件移動時很常見的)時,只需要改變控制代碼中的例項資料指標,而引用中儲存的控制代碼地址無需改變

    缺點:因為引用物件通過兩次指標定位,速度較直接指標訪問慢

②.直接指標:使用直接指標訪問時,reference中儲存的是物件地址:

  優點:節省了一次指標定位的操作,速度較快。