1. 程式人生 > >JVM鎖優化

JVM鎖優化

一次 共享 喚醒 也不會 class 記錄 發現 clas 即時編譯器

1.鎖優化

  • 掛起線程和恢復線程的開銷較大,對於鎖定狀態時間較短的情況下,掛起線程並不值得。
  1. 自旋鎖與它的自適應自旋
    • 遇到鎖不會掛起,而是忙循環(自旋)一會兒,避免了一次線程切換的開銷,但是仍在占用CPU時間。
    • 1.6默認開啟,默認自旋10次。
    • 1.6還引入了自適應自旋鎖,他可以根據上一次在同一個鎖上的自旋時間調整自旋次數。
    • 自旋失敗則進入正常的掛起線程。
  2. 鎖消除
    • JIT即時編譯器在運行時如果發現某塊代碼上有同步,但是檢測到該共享區域不可能存在競爭,就會進行鎖消除。
    • 如對某個局部變量操作時加了鎖,但是局部變量不可能逃逸出方法,所以2個線程不可能對同一個局部變量存在競爭。此時就會消除鎖。
    • JIT編譯說明這段代碼是熱點代碼,消除鎖之後對性能提高有所幫助。
  3. 鎖粗化
    • 對一個對象反復的加鎖解鎖。如StringBuff的append()方法,編譯器會優化到最外面一個鎖。
  4. 輕量級鎖
    • jvm實現無競爭情況下使用CAS操作消除同步使用的互斥量。
    • 輕量級鎖為什麽可以提高性能就是因為 “絕大多數部分的鎖在整個同步周期內都是不存在競爭的”,這是一個經驗數據,如果不存在競爭使用CAS操作就可以避免使用同步互斥量的開銷。
  5. 偏向鎖
    • 輕量級鎖是無競爭情況下使用CAS操作取消使用同步互斥量,而偏向鎖是無競爭情況下取消整個同步,連CAS都不用做。
    • 偏向鎖會偏向第一個獲得他的線程,如果該鎖沒有被其他線程獲取,那麽持有偏向鎖的線程就不會進行同步。

2.對象頭

  • 我們所說的某個對象的鎖其實就是該對象的對象頭中的幾個標誌位,該標誌位改變為某個值說明該對象的鎖被線程拿走了,釋放鎖後標誌位恢復。
  • 對象頭分為2部分,第一部分存儲對象自身運行時數據,第二部分存儲類型指針。
  1. 自身運行時數據(Mark Word):如哈希碼、GC分代年齡、鎖轉態標誌、線程持有的鎖,偏向線程ID、偏向時間戳。Mark Word是非固定的數據結構,以便存儲更多信息,根據對象狀態不同各個信息所占位數會變化,但總體肯定是8字節倍數。32位機下Mark Word占32bit,64位機下占64bit。
  2. 類型指針:指向元數據(Class類數據)的指針
  3. 如果對象是數組那麽對象頭還有一塊用於記錄數組長度的區域,因為普通Java對象通過元數據可以確定大小,而數組的元數據無法確定數組大小。

2.1 Mark Word 的不同狀態下存儲不同內容。

狀態 標誌位 存儲內容
未鎖定 01 對象哈希碼。對象分代年齡
輕量級鎖定 00 指向鎖記錄的指針
膨脹(重量級鎖定) 10 指向重量級鎖的指針
GC標記 11 空,不需要記錄信息
可偏向 01 偏向線程ID、偏向時間戳、對象分代年齡

2.2 不同狀態下不同信息所占的位數和位置

技術分享圖片

3.輕量級鎖的實現和加鎖過程。

  • 程序並不是一遇到同步代碼塊立刻就拿到對象的重量級鎖。
  1. 加鎖

    1. 代碼進入同步塊時如果鎖對象的標誌位時01那麽虛擬機會在當前線程的棧幀中建立一個 鎖記錄 空間 Lock Record,用來存儲鎖對象目前的Mark Word 的拷貝。
    2. 然後執行CAS 操作 ,它會比較鎖對象的Mark Word 與拷貝是否相等。如果相等執行3,如果不等執行5
    3. 如果相等將鎖對象的Mark Word 中的存儲內容替換為 指向棧幀中拷貝的指針,此時這個線程就獲得了該對象的鎖,並且對象的鎖標誌位改為00。
    4. 此時棧幀中的Mark Word 存儲的內容是(對象的hashCode,分代年齡,偏向鎖位)而鎖對象Mark Word 存儲的內容是(指向棧中鎖記錄的指針),而且前者鎖標誌位01,後者鎖標誌位00.
    5. 如果不相等首先檢查鎖對象Mark Word內容是否指向當前線程,如果指向執行6,如果沒有指向執行7
    6. 如果指向說明當前線程已經拿到了鎖,則可以進入同步代碼塊執行,比如同步方法中調用同步方法,並且使用的同一個鎖對象。
    7. 如果沒有指向當前線程說明鎖已經被別的線程拿到了,此時鎖標誌位改為10,鎖對象Mark Word內容換為指向互斥量(重量級鎖synchronized)的指針,然後執行8
    8. 註意此時線程發現拿不到鎖也不會立刻被掛起,它會加入自旋,如果自旋一定次數失敗就會進入阻塞狀態。
  2. 解鎖

    1. 首先查看鎖對象的Mark Word內容是否指向棧,如果是那麽就交換兩者,同步代碼執行完成。
    2. 如果沒有,說明有別的線程來拿過鎖,所以要解除互斥量同時喚醒被掛起的線程。
    3. synchronized是不公平的不會按先後掛起順序喚醒。

4.偏向鎖

技術分享圖片

  1. -XX:+UseBiasedLocking 啟用偏向鎖,1.6默認
  2. 鎖對象第一次被線程獲得,會把標誌位設為01,此時線程ID還是空,對象還未鎖定
  3. 通過一次CAS操作會把獲取到這個鎖對象的線程ID存入鎖對象的Mark Word。此時對象已被鎖定。
  4. 之後持有偏向鎖的線程進入同步代碼就不需要任何同步操作了。
  5. 如果另一個線程來獲取鎖,那麽偏向模式就結束,分別如圖按2種不同情況作出不同反應。

JVM鎖優化