JVM中對象的創建過程

參考書籍:《深入理解Java虛擬機》

參考博客:http://blog.csdn.net/tjiyu/article/details/53923392

下面我們詳細瞭解Java程序中new一個普通對象時,HotSpot虛擬機是怎麼樣創建這個對象的,包括5個步驟:相應類加載檢查過程、在Java堆中爲對象分配內存、分配後內存初始化爲零、對對象進行必要的設置、以及執行對象實例方法<init>,最後我們再從JVM指令角度來解釋下Java對象創建。

   1、相應類加載檢查過程

 通過《JVM字節碼指令及反編譯分析》可以知道:Java程序中的“new”操作會轉換爲Class文件中方法的“new”字節指令。JVM(本文特指HotSpot)遇到new指令時,先檢查指令參數是否能在常量池中定位到一個類的符號引用

 (A)、如果能定位到,檢查這個符號引用代表的類是否已被加載、解析和初始化過;

 (B)、如果不能定位到,或沒有檢查到,就先執行相應的類加載過程;

2、爲對象分配內存

   對象所需內存的大小在類加載完成後便完全確定(JVM可以通過普通Java對象的類元數據信息確定對象大小);

  爲對象分配內存相當於把一塊確定大小的內存從Java堆裏劃分出來;

(A)、分配方式:

(I)、指針碰撞

      如果Java堆是絕對規整的:一邊是用過的內存,一邊是空閒的內存,中間一個指針作爲邊界指示器;

      分配內存只需向空閒那邊移動指針,這種分配方式稱爲"指針碰撞"(Bump the Pointer);

(II)、空閒列表

      如果Java堆不是規整的:用過的和空閒的內存相互交錯;

      需要維護一個列表,記錄哪些內存可用;

      分配內存時查表找到一個足夠大的內存,並更新列表,這種分配方式稱爲"空閒列表"(Free List);

      Java堆是否規整由JVM採用的垃圾收集器是否帶有壓縮功能決定的。 所以,使用Serial、ParNew等帶Compact過程的收集器時,JVM       採用指針碰撞方式分配內存;而使用CMS這種基於標記-清除(Mark-Sweep)算法的收集器時,採用空閒列表方式;後面再介紹垃圾       收集算法和垃圾收集器,瞭解垃圾收集時應注意這裏的內容;

(B)、線程安全問題

      併發時,上面兩種方式分配內存的操作都不是線程安全的,有兩種解決方案:

(I)、同步處理

      對分配內存的動作進行同步處理:

      JVM採用CAS(Compare and Swap)機制加上失敗重試的方式,保證更新操作的原子性;

      CAS:有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改爲B,否則什麼都不做;

(II)、本地線程分配緩衝區

      把分配內存的動作按照線程劃分在不同的空間中進行:

      在每個線程在Java堆預先分配一小塊內存,稱爲本地線程分配緩衝區(Thread Local Allocation Buffer,TLAB);

      哪個線程需要分配內存就從哪個線程的TLAB上分配;

      只有TLAB用完需要分配新的TLAB時,才需要同步處理;

JVM通過"-XX:+/-UseTLAB"指定是否使用TLAB;

3、對象內存初始化爲零

 對象內存初始化爲零,但不包括對象頭; 如果使用TLAB,提前至分配TLAB時;這保證了程序中對象(及實例變量)不顯式初始賦零值,程序也能訪問到零值;

4、對象內存初始化爲零

 主要設置對象頭信息,包括類元數據引用、對象的哈希碼、對象的GC分代年齡等(詳見下節);

5、執行對象實例方法<init>

該方法把對象(實例變量)按照程序中定義的初始賦值進行初始化; 通常,經過上面5步對象才完全new出來。另外,還可以參考HotSpot虛擬機源碼中的"bytecodeInterpreter.cpp"文件,這個文件有表示解釋器處理"new"指令基本類似上面5個過程。

6、Java對象創建的JVM指令

  通過前面一些文章,我們還可以從JVM指令的角度來看對象的創建過程:

(A)、new指令

         "new"指令有一個類符號引用的常量,JVM解析該常量也就對應步驟1"相應類加載檢查過程";

         "new"指令執行完畢後,一個代表(指向)該對象實例內存數據的reference類型變量數據將壓入到操作數棧中;

(B)、dup指令

         接着會執行"dup"指令複製該reference數據,這時操作數棧棧頂就有兩個指向該對象實例內存的reference數據;              (如果             <init>方法有參數,還需要把參數加載到操作棧)

(C)、invokespecial指令

         再執行"invokespecial"指令調用對象實例方法<init>,這時操作數棧最上面的一個reference數據會出棧(如果有參數,包括方法參           數); 然後在Java虛擬機棧中創建<init>方法的棧幀,把出棧的reference數據(和參數)放入該棧幀的局部變量表中,該reference         數據在方法中也就是"this",表示對該對象實例進行的操作: 當然這些參數的數值、數據類型和順序都必須遵循實例方法的描述符中         的描述;另外,操作數棧中還有一個對象reference數據一般被“astore”到局部變量表或保存到字段變量,給後面訪問對象使用。

 

 到這裏,我們大體瞭解Java對象在HotSpot虛擬機中的創建過程, 後面我們將分別去了解:對象的內存佈局、對象的訪問定位、方法的  調用與執行、JIT編譯、以及JVM垃圾收集相關內容……


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章