上一篇文章中我們從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各個位元位區間的含義。
同樣,在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
關於為什麼要定義這麼多狀態,上面提到的輕量級鎖、重量級鎖、偏向鎖以及他們之前的關係,會在下一篇文章中重點闡述,敬請期待。