深入淺出JVM的鎖優化案例

鎖優化

適應性自旋(Adaptive Spinning)

線程阻塞的時候,讓等待的線程不放棄cpu執行時間,而是執行一個自旋(一般是空循環),這叫做自旋鎖。

自旋等待本身雖然避免了線程切換的開銷,但它是要佔用處理器時間的,因此,如果鎖被佔用的時間很短,自旋等待的效果就非常好,反之,如果鎖被佔用的時間很長,那麼自旋的線程只會白白消耗處理器資源,帶來性能上的浪費。

因此,自旋等待的時間必須要有一定的限度。如果自旋超過了限定的次數仍然沒有成功獲得鎖,就應當使用傳統的方式去掛起線程了。自旋次數的默認值是10次,用戶可以使用參數-XX:PreBlockSpin來更改。

JDK1.6引入了自適應的自旋鎖。自適應意味著自旋的時間不再固定了,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態來決定。比如前一次自旋了3次就獲得了一個鎖,那麼下一次虛擬機會允許他自旋更多次來獲得這個鎖。如果一個鎖很少能通過自旋成功獲得,那麼之後再遇到這個情況就會省略自旋過程了。

鎖消除(Lock Elimination)

虛擬機即時編譯器在運行時,對一些代碼上要求同步,但是被檢測到不可能存在共享數據競爭的鎖進行消除。一般根據逃逸分析的數據支持來作為判定依據。

鎖粗化(Lock Coarsening)

原則上,我們在編寫代碼的時候,總是推薦將同步塊的作用範圍限制得儘量小——只在共享數據的實際作用域中才進行同步,這樣是為了使需要同步的操作數量儘可能變小,如果存在鎖競爭,那等待鎖的線程也能儘快拿到鎖。

但如果一系列操作頻繁對同一個對象加鎖解鎖,或者加鎖操作再循環體內,會耗費性能,這時虛擬機會擴大加鎖範圍。

輕量級鎖(Lightweight Locking)

輕量級鎖是JDK 1.6之中加入的新型鎖機制。它的作用是在沒有多線程競爭的前提下,減少傳統的重量級鎖使用操作系統互斥量產生的性能消耗。

HotSpot虛擬機的對象頭(Object Header)分為兩部分信息,第一部分用於存儲對象自身的運行時數據,這部分稱為Mark Word。還有一部分存儲指向方法區對象類型數據的指針。

深入淺出JVM的鎖優化案例

加鎖

在代碼進入同步塊的時候,如果此同步對象沒有被鎖定(鎖標誌位為“01”狀態),虛擬機首先將在當前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用於存儲鎖對象目前的Mark Word的拷貝(官方把這份拷貝加了一個Displaced前綴,即Displaced Mark Word)。然後,虛擬機將使用CAS操作嘗試將對象的Mark Word更新為指向Lock Record的指針。如果這個更新動作成功,那麼這個線程就擁有了該對象的鎖,並且對象Mark Word的鎖標誌位(Mark Word的最後2bit)將轉變為“00”,即表示此對象處於輕量級鎖定狀態。如果這個更新操作失敗了,虛擬機首先會檢查對象的Mark Word是否指向當前線程的棧幀,如果是說明當前線程已經擁有了這個對象的鎖,那就可以直接進入同步塊繼續執行,否則說明這個鎖對象已經被其他線程搶佔了。如果有兩條以上的線程爭用同一個鎖,那輕量級鎖就不再有效,要膨脹為重量級鎖,鎖標誌的狀態值變為“10”,Mark Word中存儲的就是指向重量級鎖(互斥量)的指針,後面等待鎖的線程也要進入阻塞狀態。

解鎖

解鎖過程也是通過CAS操作來進行的。如果對象的Mark Word仍然指向著線程的鎖記錄,那就用CAS操作把對象當前的Mark Word和線程中複製的Displaced Mark Word替換回來,如果替換成功,整個同步過程就完成了。如果替換失敗,說明有其他線程嘗試過獲取該鎖,那就要在釋放鎖的同時,喚醒被掛起的線程。

性能

沒有鎖競爭時,輕量級鎖用CAS操作替代互斥量的開銷,性能較優。有鎖競爭時,除了互斥量開銷,還有CAS操作開銷,所以性能較差。但是,一般情況下,在整個同步週期內都是不存在競爭的”,這是一個經驗數據。

偏向鎖(Biased Locking)

偏向鎖也是JDK1.6中引入的鎖優化,它的目的是消除數據在無競爭情況下的同步原語,進一步提高程序的運行性能。如果說輕量級鎖是在無競爭的情況下使用CAS操作去消除同步使用的互斥量,那偏向鎖就是在無競爭的情況下把整個同步都消除掉,連CAS操作都不做了。

當鎖對象第一次被線程獲取的時候,虛擬機將會把對象頭中的標誌位設為“01”,即偏向模式。同時使用CAS操作把獲取到這個鎖的線程的ID記錄在對象的Mark Word之中,如果CAS操作成功,持有偏向鎖的線程以後每次進入這個鎖相關的同步塊時,虛擬機都可以不再進行任何同步操作。當有另外一個線程去嘗試獲取這個鎖時,偏向模式結束。

偏向鎖可以提高帶有同步但無競爭的程序性能,但並不一定總是對程序運行有利。如果程序中大多數的鎖總是被多個不同的線程訪問,那偏向模式就是多餘的。在具體問題具體分析的前提下,有時候使用參數-XX:-UseBiasedLocking來禁止偏向鎖優化反而可以提升性能。


分享到:


相關文章: