深入理解JVM(二)HotSpot虛擬機對象探祕

1.對象的創建過程

1.1.當虛擬機遇到一條含有new的指令時,首先檢查常量池中是否有即將要創建的這個對象所屬的類的符號引用;若常量池中沒有這個類的符號引用,說明這個類還沒有被定義!拋出ClassNotFoundException;進而檢查這個符號引用所代表的類是否已經被JVM加載,解析,初始化;若該類還沒有被加載,就找該類的class文件,並加載進方法區;若該類已經被JVM加載,則準備爲對象分配內存;

1.2對象所需內存的大小在類加載完成後便可以完全確定。爲對象分配空間的任務等同於把一塊確定大小的內存從Java堆中劃分出來。從堆中劃分一塊對應大小的內存空間給新的對象;分配堆中內存有兩種方式:

1.2.1指針碰撞

假設Java堆中內存是絕對完整的,所有用過的內存都放到一邊,空閒的內存放到另一邊,中間放着一個指針作爲分界點的指示器,那所分配內存就僅僅是把那個指針向空閒空間那邊挪動一段與對象大小相等的距離,這種分配方式成爲"指針碰撞",其中使用這種分配方式的有Serial、ParNew收集器即採用複製算法或標記-整理算法等。

1.2.2空閒列表

如果Java堆中內存不是完整的,那麼堆中空閒區域和已使用區域交錯,因此需要用一張“空閒列表”來記錄堆中哪些區域是空閒區域,從而在創建對象的時候根據這張“空閒列表”找到空閒區域,並分配內存,採用這種分配方式的收集器採用標記-清除算法。

綜上所述:JVM究竟採用哪種內存分配方法,取決於它使用了何種垃圾收集器。

1.3.內存分配完成後,虛擬機需要將分配的內存空間都初始化爲零值。

1.4.爲對象中的成員變量賦上初始值(默認初始化);

1.5.設置對象頭中的信息;

1.6.調用對象的構造函數進行初始化,此時,整個對象的創建過程就完成了。

2.對象的內存佈局

在HotSpot虛擬機中,對象分爲3塊區域:對象頭、實例數據、對其填充。

2.1. 對象頭

2.1.1.對象頭中一部分用於存自身的運行時數據:哈希碼、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等。這部分數據的長度官方稱爲"Mark Word"

2.1.2對象頭中的另一部分是類型指針(並不一定必須有)。通過該指針能確定這個對象所屬哪個類。此外,如果對象是一個數組,那麼對象頭中還要包含數組長度。

2.2實例數據

實力數據部分就是成員變量的值,其中包含父類的成員變量和本類的成員變量。

2.3對其填充

對其填充並不是必然存在的也沒有特別的含義,它僅僅起着佔位符的作用。HotSpot要求對象的總長度必須是8字節的整數倍。由於對象頭一定是8字節的整數倍,但實例數據部分的長度是任意的,因此需要對齊補充字段確保整個對象的總長度爲8的整數倍。

3對象的訪問

目前主流的訪問方式有兩種:使用句柄和直接指針兩種。

3.1句柄訪問

java堆中會劃分出一塊內存作爲句柄池,引用類型中存儲的是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址信息。採用句柄訪問的最大好處是引用類型中存儲的是穩定的句柄地址,在對象被移動時只會改變句柄中的實例數據指針,而引用類型本身不需要改變。

3.2直接指針

引用類型的變量直接存放對象的地址,從而不需要句柄池,通過引用能夠直接訪問對象。但對象所在的內存空間中需要額外的策略存儲對象所屬的類信息的地址。

HotSpot採用直接指針方式訪問對象,因爲它只需一次尋址操作,節省了一次指針定位的時間開銷從而性能比句柄訪問方式快一倍。但它需要額外的策略存儲對象在方法區中類信息的地址。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章