天天看點

Hotspot對象1. 對象的建立2. 對象的記憶體布局3. 對象的通路定位

1. 對象的建立

對象的建立過程:

Hotspot對象1. 對象的建立2. 對象的記憶體布局3. 對象的通路定位

1.1 類加載檢查

虛拟機遇到一條new指令時,先檢查這個指令的參數能否在常量池中定位到一個類的符号引用,并檢查這個符号引用代表的類是否已被加載、解析和初始化過。如果沒有,則先進行類的加載過程。

1.2 配置設定記憶體

對象所需的記憶體大小在類加載完成就可以完全确定,為對象配置設定記憶體空間的任務等同于從堆中劃分出一塊固定大小的記憶體。

配置設定記憶體有兩種方式:

指針碰撞:假設Java堆中的記憶體是規整的,用過的記憶體在一邊,空閑的在另一邊,中間有一個指針作為分界點的訓示器,
    所配置設定的記憶體就把那個指針向空閑那邊挪動一段與對象大小相等的距離。

空閑清單:如果Java堆中的記憶體不是規整的,虛拟機必須維護一個清單,記錄哪些記憶體塊可用的,
    配置設定時從清單中找到一塊足夠大的空間劃分給對象,并更新清單的記錄。           

選擇哪種配置設定方式由java堆是否規整決定,而java堆是否規整又由所采用的垃圾收集器是否帶有壓縮整理的功能決定。是以Serial、ParNew等帶有Compact過程的收集器時,采用的是指針碰撞,而使用CMS這種基于mark-sweep算法的收集器時,通常采用空閑清單。

在劃分可用空間時,會遇到線程安全的問題,例如在指針碰撞方式中線程A和B同時修改指針。解決線程安全問題有兩種方案:

1). 對配置設定記憶體空間的動作進行同步處理:虛拟機采用CAS方式保證更新操作的原子性;

2). 把記憶體配置設定的動作按照線程劃分在不同的空間之中進行,即每個線程在Java堆中預先配置設定一小塊記憶體,稱為本地線程配置設定緩沖(TLAB)。
    哪個線程需要配置設定記憶體,就在哪個線程的TLAB上配置設定,隻有TLAB用完并配置設定新的TLAB時,才需要同步鎖定。
    是否使用TLAB可通過-XX:+UseTLAB參數來設定。           

1.3 初始化零值

将配置設定到的記憶體空間都初始化為零值(不包括對象頭),如果用TLAB,則在TLAB配置設定時初始化為零值。這一步操作保證了對象的執行個體字段在java代碼中不賦初始值就能直接使用,程式能通路到這些字段的資料類型所對應的零值。

1.4 設定對象頭

主要設定類的中繼資料資訊、對象的哈希碼、對象的GC分代年齡等資訊。

1.5 執行init方法初始化

一般new指令後會跟随invokespecial指令,當跟随invokespecial指令時,就要執行方法,該方法用于執行個體初始化,父類初始化和私有方法。把對象按照代碼中定義的進行初始化,這樣一個真正的對象才算完全産生出來。

2. 對象的記憶體布局

對象在記憶體中存儲的布局可分為三個部分:對象頭、執行個體資料和對齊填充。

2.1 對象頭

對象頭包括兩個部分:mark word和類型指針。

2.1.1 mark word

Hotspot對象1. 對象的建立2. 對象的記憶體布局3. 對象的通路定位

mark word用于存儲對象自身的運作時資料,如哈希碼(HashCode)、GC 分代年齡、鎖狀态标志、線程持有的鎖、偏向線程ID、偏向時間戳、對象分代年齡,這部分的資料長度在32位和64位的虛拟機(未開啟壓縮指針)中分别為32bit和64bit。

Mark Word被設計成一個非固定的資料結構以便在極小的空間記憶體儲盡量多的資訊,它會根據自己的狀态複用自己的存儲空間。

2.1.1 類型指針

類型指針,即對象指向它所屬類的中繼資料的指針,虛拟機通過這個指針來确定這個對象是哪個類的執行個體;如果對象是一個Java數組,那在對象頭中還必須有一塊用于記錄數組長度的資料。因為虛拟機可以通過普通Java對象的中繼資料資訊确定Java對象的大小,但是從數組的中繼資料中無法确定數組的大小。

2.2 執行個體資料

執行個體資料部分是對象真正存儲的有效資訊,也是在程式代碼中所定義的各種類型的字段内容,包括從父類中繼承的字段資訊。

這部分的存儲順序會受到虛拟機配置設定政策和字段在java代碼中定義順序的影響,Hotspot預設的配置設定政策為longs/doubles、ints、shorts/chars、bytes/booleans、oops(ordinary object pointers)。

從配置設定政策中可以看出,相同寬度的字段總是被配置設定到一起。

2.3 對齊填充

對齊填充不是必然存在的,沒有特别含義,僅僅起着占位符的作用。由于Hotspot VM的自動記憶體管理系統要求對象起始位址必須是8位元組的整數倍,即對象的大小必須是8位元組的整數倍。是以,當對象執行個體資料部分沒有對齊時,就需要對齊填充來不全。

3. 對象的通路定位

reference類型在java虛拟機規範中隻規定了一個指向對象的引用,并沒有定義這個引用應該通過何種方式去定位、通路堆中對象的具體位置。目前各虛拟機實作的通路方式有句柄和直接指針兩種。

3.1 句柄

如果使用句柄通路,Java堆中将會劃分出一塊記憶體作為句柄池,reference中存儲的就是對象的句柄位址,而句柄中包含了對象執行個體資料和對象類型資料各自的具體位址資訊,這種方式下,reference到對象類型資料進行了兩次指針定位:

Hotspot對象1. 對象的建立2. 對象的記憶體布局3. 對象的通路定位

3.2 直接指針

如果使用直接指針通路,那麼Java堆對象的布局必須考慮如何放置通路類型的資料的相關資訊,而reference中存儲的直接就是對象位址:

Hotspot對象1. 對象的建立2. 對象的記憶體布局3. 對象的通路定位

這兩種對象通路方式各有優劣,句柄的最大好處是reference中存儲的是穩定的句柄位址,在對象被移動(垃圾收集時移動對象是非常普遍的行為)時隻會改變句柄中的執行個體資料的指針,而reference本身不需要修改。

使用直接指針方式的好處是速度快,節省了一次指針定位的時間開銷,由于對象的通路在Java中非常的頻繁,是以這類開銷積少成多後也是一項非常可觀的執行成本。HotSpot就是使用的是直接指針通路方式。

參考:《深入了解java虛拟機》