JVM的鎖優化
鎖優化技術(HotSpot虛擬機器而言)包括適應性自旋、鎖消除、鎖粗化、輕量級鎖和偏向鎖等。這些技術都是為了線上程之間更高效地共享資料以及解決競爭問題,從而提高程式效率。
自旋鎖
什麼是自旋鎖?
如果執行緒獲取不到鎖,第一時間不是去切換系統態進行等待,而是做一個迴圈操作,去等到鎖的釋放,迴圈到一定的次數終止迴圈,調入系統呼叫。為了讓執行緒等待,而不是阻塞,讓執行緒執行一個忙迴圈(自旋),這就是自旋鎖。
為什麼消耗CPU而不是等待?
自旋鎖的優化主要是為了減少了執行緒切換帶來的消耗。
自旋鎖適應的場景
自旋鎖適合那些執行緒佔用鎖時間短的場景。
什麼是自適應自旋鎖?
JDK1.6加入了自適應自旋鎖。顧名思義,如果在同一個鎖物件上,自旋等待剛剛成功獲得過鎖,並且持有鎖的執行緒正在執行,那麼虛擬機器就會認為這次自旋也很有可能再次成功,並將自旋等待時間延長。如果對於某個鎖,自旋很少成功,那麼在之後獲取該鎖時可能會放棄不自旋直接掛起執行緒。
鎖消除
什麼是鎖消除?
指虛擬機器即時編譯器在執行時,對一些程式碼上要求同步,但是被檢測到不可能存在共享資料競爭時,把鎖進行消除。
怎樣判斷哪些程式碼可以進行鎖消除?
鎖消除的主要判斷依據來源於逃逸分析的資料支援,如果判斷在一段程式碼中,堆上的所有資料都不會逃逸出去從而被其他執行緒訪問到,那就可以把它們當做棧上資料對待,認為它們是執行緒私有的,同步加鎖就無需進行。
鎖粗化
什麼是鎖粗化?
如果一系列的連續動作都對同一物件反覆加鎖和解鎖,甚至加鎖操作時出現在迴圈體中的,那即使沒有執行緒競爭,頻繁地進行互斥同步操作也會導致不必要的效能損耗,比如連續的append()方法。
輕量級鎖和偏向鎖
在說輕量級鎖和偏向鎖之前,我們要先了解一下HotSpot虛擬機器中物件的物件頭。
HotSpot虛擬機器的物件頭分為兩部分資訊:
- 第一部分用域儲存物件自身的執行時資料(Mark Word),如雜湊碼、GC分代年齡等,他是實現輕量級鎖和偏向鎖的關鍵。
- 另一部分用於儲存只想方法區物件型別的指標,如果是陣列物件,還有一個額外部分儲存陣列長度。
所以,我們來看一下物件頭的Mark Word中的儲存內容有哪些?
儲存內容 | 標誌位 | 狀態 | 用處 |
物件雜湊碼,物件分代年齡 | 01 | 未鎖定 | 狀態 |
指向鎖記錄的指標 | 00 | 輕量級鎖定 | 狀態 |
指向重量級鎖的指標 | 10 | 膨脹 | 狀態 |
空,不需要記錄資訊 | 11 | GC標記 | 狀態 |
偏向執行緒ID,偏向時間戳、物件分代年齡 | 01 | 可偏向 | 狀態 |
什麼是CAS操作?
CAS是英文單詞CompareAndSwap的縮寫,中文意思是:比較並替換。簡單來說,CAS整個比較並替換的操作是一個原子操作。
輕量級鎖
什麼是輕量級鎖?
他的意思是在沒有多執行緒競爭的前提下,減少傳統的重量級鎖使用作業系統互斥量產生的消耗
輕量級鎖的執行過程?
加鎖過程:
- 進入同步塊的時候,如果此同步物件沒有被鎖定(鎖標誌位為“01”狀態),虛擬機器首先將在當前執行緒的幀棧中建立一個名為“鎖空間”(Lock Record)的空間,用於儲存鎖物件目前Mark Word的拷貝(這份拷貝叫Displaced Mark Word)
- 虛擬機器將使用CAS操作嘗試將物件的Mark Word更新為指向Lock Record的指標
- 如果成功,這個執行緒就有了該物件的輕量級鎖,Mark Word的鎖標誌位變為“00”
- 如果失敗,虛擬機器先檢查物件的Mark Word是否指向當前執行緒的棧幀
- 如果指向當前執行緒幀棧就說明該執行緒獲取到了該鎖,可進入同步塊繼續執行
- 若沒指向當前執行緒的幀棧,表示這個鎖物件被其他執行緒佔用,這時輕量級鎖不再有效,要膨脹為重量級鎖,鎖的標誌位變為“10”,Mark Word 中儲存的就是指向重量級鎖(互斥量)的指標,後面等待的執行緒也要進入阻塞狀態
解鎖過程:
- 如果物件的Mark Word仍然指向執行緒的鎖記錄,就用CAS操作把物件當前Mark Word和執行緒中複製的Displaced Mark Word替換回來
- 替換成功,同步完成
- 替換失敗,有其他執行緒嘗試獲取該鎖,釋放鎖的同時,喚醒被掛起的執行緒。
輕量級鎖的使用場景
提升程式同步效能的依據是“對於絕大部分的鎖,在整個同步週期內都是不存在競爭的”。
如果沒有競爭,輕量級鎖使用CAS操作避免了使用互斥量的開銷。
但如果存在鎖競爭,除了互斥量的開銷外,還額外發生了CAS操作,因此在有競爭的情況下,輕量級鎖會比傳統的重量級鎖更慢。
偏向鎖
什麼是偏向鎖?
消除資料在無競爭情況下的同步原語,進一步提高程式的執行效能。簡單來說就是**在無競爭的情況下把整個同步都消除掉,連CAS操作都不做。**就是說當一個執行緒率先獲取到這個鎖後,如果該鎖沒有被其他執行緒獲取,那麼持有偏向鎖的執行緒將永遠不需要同步。
工作過程
- 當鎖物件第一次被執行緒獲取的時候,虛擬機器將會把物件頭中的標誌位設為"01",使用CAS操作把獲取到的鎖的執行緒ID記錄到Mark Word中,如果CAS操作成功,持有偏向鎖的執行緒以後每次進入這個鎖相關的同步塊時,都不再進行任何操作
- 當有另一個執行緒去嘗試獲取這個鎖時,偏向模式宣告結束。根據鎖物件目前是否處於被鎖定的狀態,撤銷偏向後恢復到未鎖定或輕量級鎖定的狀態,後續的同步操作就如上面介紹的輕量級鎖那樣執行
偏向鎖的使用場景
可以提高帶有同步但無競爭的程式效能
和輕量級鎖一樣,如果這個同步塊可能很多時候都是多個執行緒同時訪問的話,那麼偏向鎖甚至會增加時耗
總結
JVM對鎖進行了優化,包括自旋鎖(自適應自旋鎖)、鎖粗化、輕量級鎖、偏向鎖。而後兩者會因為競爭的多少而產生效能的變化。