天天看點

深入了解多線程(三)—— Java的對象頭

上一篇文章中我們從HotSpot的源碼入手,介紹了Java的對象模型。這一篇文章在上一篇文章的基礎上再來介紹一下Java的對象頭。主要介紹一下對象頭的作用,結構以及他和鎖的關系。

Java對象模型回顧與勘誤

在上一篇文章中,關于對象頭的部分描述有誤,我已經在我部落格的文章中就行修正 。這裡再重新表述一下。

每一個Java類,在被JVM加載的時候,JVM會給這個類建立一個

instanceKlass

,儲存在方法區,用來在JVM層表示該Java類。當我們在Java代碼中,使用new建立一個對象的時候,JVM會建立一個

instanceOopDesc

對象,這個對象中包含了對象頭以及執行個體資料。

這裡提到的對象頭到底是什麼呢?

class oopDesc {
  friend class VMStructs;
 private:
  volatile markOop  _mark;
  union _metadata {
    wideKlassOop    _klass;
    narrowOop       _compressed_klass;
  } _metadata;
}           

上面代碼中的

_mark

_metadata

其實就是對象頭的定義。關于

_metadata

之前就介紹過,這裡不再贅述。由于這個專題主要想介紹和JAVA并發相關的知識,是以本文展開介紹一下

_mark

 ,即mark word。

對象頭資訊是與對象自身定義的資料無關的額外存儲成本,考慮到虛拟機的空間效率,Mark Word被設計成一個非固定的資料結構以便在極小的空間記憶體儲盡量多的資訊,它會根據對象的狀态複用自己的存儲空間。

對markword的設計方式上,非常像網絡協定封包頭:将mark word劃分為多個比特位區間,并在不同的對象狀态下賦予比特位不同的含義。下圖描述了在32位虛拟機上,在對象不同狀态時 mark word各個比特位區間的含義。

深入了解多線程(三)—— Java的對象頭

同樣,在HotSpot的源碼中我們可以找到關于對象頭對象的定義,會一一印證上圖的描述。對應與markOop.hpp類。

enum { age_bits                 = 4,
      lock_bits                = 2,
      biased_lock_bits         = 1,
      max_hash_bits            = BitsPerWord - age_bits - lock_bits - biased_lock_bits,
      hash_bits                = max_hash_bits > 31 ? 31 : max_hash_bits,
      cms_bits                 = LP64_ONLY(1) NOT_LP64(0),
      epoch_bits               = 2
};           

從上面的枚舉定義中可以看出,對象頭中主要包含了GC分代年齡、鎖狀态标記、哈希碼、epoch等資訊。

從上圖中可以看出,對象的狀态一共有五種,分别是無鎖态、輕量級鎖、重量級鎖、GC标記和偏向鎖。在32位的虛拟機中有兩個Bits是用來存儲鎖的标記為的,但是我們都知道,兩個bits最多隻能表示四種狀态:00、01、10、11,那麼第五種狀态如何表示呢 ,就要額外依賴1Bit的空間,使用0和1來區分。

在32位的HotSpot虛拟機 中對象未被鎖定的狀态下,Mark Word的32個Bits空間中的25Bits用于存儲對象哈希碼(HashCode),4Bits用于存儲對象分代年齡,2Bits用于存儲鎖标志位,1Bit固定為0,表示非偏向鎖。

markOop.hpp類中有關于對象狀态的定義:

enum { locked_value             = 0,
         unlocked_value           = 1,
         monitor_value            = 2,
         marked_value             = 3,
         biased_lock_pattern      = 5
  };           

簡單翻譯一下:

locked_value(00) = 0

unlocked_value(01) = 1

monitor_value(10) = 2

marked_value(11) = 3

biased_lock_pattern(101) = 5