1. 程式人生 > >Java中物件頭詳解

Java中物件頭詳解

一:物件頭

HotSpot虛擬機器中,物件在記憶體中儲存的佈局可以分為三塊區域:物件頭(Header)、例項資料(Instance Data)和對齊填充(Padding)。  

HotSpot虛擬機器的物件頭(Object Header)包括兩部分資訊,第一部分用於儲存物件自身的執行時資料, 如雜湊碼(HashCode)、GC分代年齡、鎖狀態標誌、執行緒持有的鎖、偏向執行緒ID、偏向時間戳等等,這部分資料的長度在32位和64位的虛擬機器(暫 不考慮開啟壓縮指標的場景)中分別為32個和64個Bits,官方稱它為“Mark Word”。

物件需要儲存的執行時資料很多,其實已經超出了32、64位Bitmap結構所能記錄的限度,但是物件頭資訊是與物件自身定義的資料無關的額 外儲存成本,考慮到虛擬機器的空間效率,Mark Word被設計成一個非固定的

資料結構以便在極小的空間記憶體儲儘量多的資訊,它會根據物件的狀態複用自己的儲存空間。例如在32位的HotSpot虛擬機器 中物件未被鎖定的狀態下,Mark Word的32個Bits空間中的25Bits用於儲存物件雜湊碼(HashCode),4Bits用於儲存物件分代年齡,2Bits用於儲存鎖標誌 位,1Bit固定為0,在其他狀態(輕量級鎖定、重量級鎖定、GC標記、可偏向)下物件的儲存內容如下表所示。  

物件頭的另外一部分是型別指標,即是物件指向它的類的元資料的指標,虛擬機器通過這個指標來確定這個物件是哪個類的例項。並不是所有的虛擬機器實現都必須在物件資料上保留型別指標,換句話說查詢物件的元資料資訊並不一定要經過物件本身。另外,如果物件是一個

Java陣列,那在物件頭中還必須有一塊用於記錄陣列長度的資料,因為虛擬機器可以通過普通Java物件的元資料資訊確定Java物件的大小,但是從陣列的元資料中無法確定陣列的大小。  

 

這裡要特別關注的是鎖標誌位,  鎖標誌位與是否偏向鎖對應到唯一的鎖狀態。

所以鎖的狀態儲存在物件頭中,所以再理解

 

  1. Synchronized鎖的到底是什麼, 鎖住的是程式碼還是物件)(答案鎖的是物件)?
  2.  java中鎖,鎖的是物件,它是怎麼實現的?

 

這兩個問題,就好懂了!

 

二:鎖的狀態

鎖的狀態總共有四種:無鎖狀態、偏向鎖、輕量級鎖和重量級鎖。隨著鎖的競爭,鎖可以從偏向鎖升級到輕量級鎖,再升級的重量級鎖(但是鎖的升級是單向的,也就是說只能從低到高升級,不會出現鎖的降級)。JDK 1.6中預設是開啟偏向鎖和輕量級鎖的,我們也可以通過-XX:-UseBiasedLocking來禁用偏向鎖。

 

1、輕量級鎖的加鎖過程

  (1)在程式碼進入同步塊的時候,如果同步物件鎖狀態為無鎖狀態(鎖標誌位為“01”狀態,是否為偏向鎖為“0”),虛擬機器首先將在當前執行緒的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用於儲存鎖物件目前的Mark Word的拷貝,官方稱之為 Displaced Mark Word。這時候執行緒堆疊與物件頭的狀態如圖2.1所示。

  (2)拷貝物件頭中的Mark Word複製到鎖記錄中。

  (3)拷貝成功後,虛擬機器將使用CAS操作嘗試將物件的Mark Word更新為指向Lock Record的指標,並將Lock record裡的owner指標指向object mark word。如果更新成功,則執行步驟(4),否則執行步驟(5)。

  (4)如果這個更新動作成功了,那麼這個執行緒就擁有了該物件的鎖,並且物件Mark Word的鎖標誌位設定為“00”,即表示此物件處於輕量級鎖定狀態,這時候執行緒堆疊與物件頭的狀態如圖2.2所示。

  (5)如果這個更新操作失敗了,虛擬機器首先會檢查物件的Mark Word是否指向當前執行緒的棧幀,如果是就說明當前執行緒已經擁有了這個物件的鎖,那就可以直接進入同步塊繼續執行。否則說明多個執行緒競爭鎖,輕量級鎖就要膨脹為重量級鎖,鎖標誌的狀態值變為“10”,Mark Word中儲存的就是指向重量級鎖(互斥量)的指標,後面等待鎖的執行緒也要進入阻塞狀態。 而當前執行緒便嘗試使用自旋來獲取鎖,自旋就是為了不讓執行緒阻塞,而採用迴圈去獲取鎖的過程。

 

  圖2.1 輕量級鎖CAS操作之前堆疊與物件的狀態

 

圖2.2 輕量級鎖CAS操作之後堆疊與物件的狀態

 

 

三、偏向鎖

  引入偏向鎖是為了在無多執行緒競爭的情況下儘量減少不必要的輕量級鎖執行路徑,因為輕量級鎖的獲取及釋放依賴多次CAS原子指令,而偏向鎖只需要在置換ThreadID的時候依賴一次CAS原子指令(由於一旦出現多執行緒競爭的情況就必須撤銷偏向鎖,所以偏向鎖的撤銷操作的效能損耗必須小於節省下來的CAS原子指令的效能消耗)。上面說過,輕量級鎖是為了線上程交替執行同步塊時提高效能,而偏向鎖則是在只有一個執行緒執行同步塊時進一步提高效能。

1、偏向鎖獲取過程:

  (1)訪問Mark Word中偏向鎖的標識是否設定成1,鎖標誌位是否為01——確認為可偏向狀態。

  (2)如果為可偏向狀態,則測試執行緒ID是否指向當前執行緒,如果是,進入步驟(5),否則進入步驟(3)。

  (3)如果執行緒ID並未指向當前執行緒,則通過CAS操作競爭鎖。如果競爭成功,則將Mark Word中執行緒ID設定為當前執行緒ID,然後執行(5);如果競爭失敗,執行(4)。

  (4)如果CAS獲取偏向鎖失敗,則表示有競爭。當到達全域性安全點(safepoint)時獲得偏向鎖的執行緒被掛起,偏向鎖升級為輕量級鎖,然後被阻塞在安全點的執行緒繼續往下執行同步程式碼。

  (5)執行同步程式碼。

2、偏向鎖的釋放:

  偏向鎖的撤銷在上述第四步驟中有提到。偏向鎖只有遇到其他執行緒嘗試競爭偏向鎖時,持有偏向鎖的執行緒才會釋放鎖,執行緒不會主動去釋放偏向鎖。偏向鎖的撤銷,需要等待全域性安全點(在這個時間點上沒有位元組碼正在執行),它會首先暫停擁有偏向鎖的執行緒,判斷鎖物件是否處於被鎖定狀態,撤銷偏向鎖後恢復到未鎖定(標誌位為“01”)或輕量級鎖(標誌位為“00”)的狀態。

3、重量級鎖、輕量級鎖和偏向鎖之間轉換

該圖主要是對上述內容的總結,如果對上述內容有較好的瞭解的話,該圖應該很容易看懂。

參考資料:

      http://blog.csdn.NET/u010723709/article/details/50341631

      http://www.cnblogs.com/paddix/p/5405678.html

      http://www.cnblogs.com/lingepeiyong/archive/2012/10/30/2745973.html