JVM中對象的內存佈局

創建對象

java程序中,創建對象的方式有很多,出了new之外,還可以通過反射機制、Object.clone、反序列化、Unsafe.allocateInstance等,當我們需要創建一個對象時,需要通過常量池定位類的符號引用,然後判斷是否執行過類加載,類加載執行通過後,JVM就會爲對象分配內存空間,空間的大小在在類加載完成時就已經確定。

對象空間的分配策略決於垃圾收集器的算法實現,對於空間規整的一般採用指針碰撞的方式分配空間,也就是指針移動指定大小的空間即可,對於空間不規整的,一般採用空閒列表,分配時指定一個足夠大的空閒列表。當然空間的分配也必須要保證線程安全,而保證線程安全的方案主要有兩種:

  1. 採用CAS配置失敗重試保證更新的原子性
  2. 另一種是內存分配的動作在按照線程劃分的不同空間進行。也就是說,我們學習過JVM內存區域就明白,堆是線程間共享的,如果我們爲每個線程預先分配一塊堆(稱爲本地線程分配緩存,TLAB),不同線程的空間分配在不同緩衝中進行,TLAB用完時才同步鎖定增加TLAB。

對象的內存佈局

對象內存空間成功分配之後就需要對對象進行必要的初始化,初始化就與對象的內存佈局有着很大的關係,對象在內存中的佈局劃分爲三個區域:對象頭、實例數據、對齊填充,如圖(來自網絡)。對象頭又分爲兩部分,一部分存儲運行時數據:哈希碼、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等,稱爲Mark Word,稍後做詳細介紹;另一部分就是類型指針,指向類元數據,表明對象所屬的類型,這樣可以節省很多的空間。

Mark Word,在不同位的JVM下有着不同的長度,就拿32JVM爲例,Mark Word的32位,中25bit用來存儲對象的哈希碼,4bit存儲分代年齡,2bit存儲鎖標識位,1bit固定爲0,這是在沒有被鎖定的情況下的Mark wor的存儲結構,在不同的鎖條件下,mark word存儲的數據會變化,如圖(來自網絡):

無鎖狀態:

不同鎖狀態下:

對象的訪問定位

學習JVM的內存區域,我們知道對象在堆中,線程執行時,引用在虛擬機棧的棧楨的局部變量表中,那麼引用是如何定位到我們的對象的,目前主流的方式有句柄和直接訪問兩種

句柄訪問:會在堆中劃出一塊內存作爲句柄池,引用存儲對象的句柄地址,句柄中包含實例數據信息和對象類型數據

直接訪問:引用直接存儲對象的地址,對象需要考慮訪問對象類型的問題

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