深入理解Java物件頭
一.物件頭包含的資訊
1.Mark Word:
這一部分主要包括 儲存物件自身的執行時資料,如雜湊碼(HashCode)、 GC分代年齡、 鎖狀態標誌、 執行緒持有的鎖、 偏向執行緒ID、 偏向時間戳等。
注意這裡的1bit,主要是表名是否是偏向鎖,如果是0的話,表示是無鎖,如果是1的話,表示是偏向鎖。
由於物件需要儲存的執行時資料很多,其實已經超出了32位、64位Bitmap結構所能記錄的限度,但是物件頭資訊是與物件自身定義的資料無關的額外儲存成本,考慮到虛擬機器的空間效率,Mark Word被設計成一個非固定的資料結構以便在極小的空間記憶體儲儘量多的資訊,它會根據物件的狀態複用自己的儲存空間。
2.型別指標:
即物件指向它的類元資料的指標,虛擬機器通過這個指標來確定這個物件是哪個類的例項。不同的虛擬機器有不同的物件訪問方式,目前主流的訪問方式有使用控制代碼和直接指標兩種。
二.物件頭的應用
synchronized用的鎖是存在Java物件頭裡的。如果物件是陣列型別,則虛擬機器用3個字寬(Word)儲存物件頭,如果物件是非陣列型別,則用2字寬儲存物件頭。在32位虛擬機器中,1字寬等於4位元組,即32bit,如表2-2所示。
JVM一般是這樣使用鎖和Mark Word的:
1,當沒有被當成鎖時,這就是一個普通的物件,Mark Word記錄物件的HashCode,鎖標誌位是01,是否偏向鎖那裡的值為0。
2,當物件被當做同步鎖並有一個執行緒A搶到了鎖時,鎖標誌位還是01,但是否偏向鎖那裡被改成1,前23bit記錄搶到鎖的執行緒id,表示進入偏向鎖狀態。
3,當執行緒A再次試圖來獲得鎖時,JVM發現同步鎖物件的標誌位是01,是否偏向鎖是1,也就是偏向狀態,Mark Word中記錄的執行緒id就是執行緒A自己的id,表示執行緒A已經獲得了這個偏向鎖,可以執行同步鎖的程式碼。
4,當執行緒B試圖獲得這個鎖時,JVM發現同步鎖處於偏向狀態,但是Mark Word中的執行緒id記錄的不是B,那麼執行緒B會先用CAS操作試圖獲得鎖,這裡的獲得鎖操作是有可能成功的,因為執行緒A一般不會自動釋放偏向鎖。如果搶鎖成功,就把Mark Word裡的執行緒id改為執行緒B的id,代表執行緒B獲得了這個偏向鎖,可以執行同步鎖程式碼。如果搶鎖失敗,則繼續執行步驟5。
5,偏向鎖狀態搶鎖失敗,代表當前鎖有一定的競爭,偏向鎖將升級為輕量級鎖。JVM會在當前執行緒的執行緒棧中開闢一塊單獨的空間,裡面儲存指向物件鎖Mark Word的指標,同時在物件鎖Mark Word中儲存指向這片空間的指標。上述兩個儲存操作都是CAS操作,如果儲存成功,代表執行緒搶到了同步鎖,就把Mark Word中的鎖標誌位改成00,可以執行同步鎖程式碼。如果儲存失敗,表示搶鎖失敗,競爭太激烈,繼續執行步驟6。
6,輕量級鎖搶鎖失敗,JVM會使用自旋鎖,自旋鎖不是一個鎖狀態,只是代表不斷的重試,嘗試搶鎖。從JDK1.7開始,自旋鎖預設啟用,自旋次數由JVM決定。如果搶鎖成功則執行同步鎖程式碼,如果失敗則繼續執行步驟7。
7,自旋鎖重試之後如果搶鎖依然失敗,同步鎖會升級至重量級鎖,鎖標誌位改為10。在這個狀態下,未搶到鎖的執行緒都會被阻塞。
參照:https://blog.csdn.net/lkforce/article/details/81128115#commentBox
《深入理解Java虛擬機器》、《Java併發程式設計的藝術》