JVM鎖優化
阿新 • • 發佈:2018-09-09
一次 共享 喚醒 也不會 class 記錄 發現 clas 即時編譯器
1.鎖優化
- 掛起線程和恢復線程的開銷較大,對於鎖定狀態時間較短的情況下,掛起線程並不值得。
- 自旋鎖與它的自適應自旋
- 遇到鎖不會掛起,而是忙循環(自旋)一會兒,避免了一次線程切換的開銷,但是仍在占用CPU時間。
- 1.6默認開啟,默認自旋10次。
- 1.6還引入了自適應自旋鎖,他可以根據上一次在同一個鎖上的自旋時間調整自旋次數。
- 自旋失敗則進入正常的掛起線程。
- 鎖消除
- JIT即時編譯器在運行時如果發現某塊代碼上有同步,但是檢測到該共享區域不可能存在競爭,就會進行鎖消除。
- 如對某個局部變量操作時加了鎖,但是局部變量不可能逃逸出方法,所以2個線程不可能對同一個局部變量存在競爭。此時就會消除鎖。
- JIT編譯說明這段代碼是熱點代碼,消除鎖之後對性能提高有所幫助。
- 鎖粗化
- 對一個對象反復的加鎖解鎖。如StringBuff的append()方法,編譯器會優化到最外面一個鎖。
- 輕量級鎖
- jvm實現無競爭情況下使用CAS操作消除同步使用的互斥量。
- 輕量級鎖為什麽可以提高性能就是因為 “絕大多數部分的鎖在整個同步周期內都是不存在競爭的”,這是一個經驗數據,如果不存在競爭使用CAS操作就可以避免使用同步互斥量的開銷。
- 偏向鎖
- 輕量級鎖是無競爭情況下使用CAS操作取消使用同步互斥量,而偏向鎖是無競爭情況下取消整個同步,連CAS都不用做。
- 偏向鎖會偏向第一個獲得他的線程,如果該鎖沒有被其他線程獲取,那麽持有偏向鎖的線程就不會進行同步。
2.對象頭
- 我們所說的某個對象的鎖其實就是該對象的對象頭中的幾個標誌位,該標誌位改變為某個值說明該對象的鎖被線程拿走了,釋放鎖後標誌位恢復。
- 對象頭分為2部分,第一部分存儲對象自身運行時數據,第二部分存儲類型指針。
- 自身運行時數據(Mark Word):如哈希碼、GC分代年齡、鎖轉態標誌、線程持有的鎖,偏向線程ID、偏向時間戳。Mark Word是非固定的數據結構,以便存儲更多信息,根據對象狀態不同各個信息所占位數會變化,但總體肯定是8字節倍數。32位機下Mark Word占32bit,64位機下占64bit。
- 類型指針:指向元數據(Class類數據)的指針
- 如果對象是數組那麽對象頭還有一塊用於記錄數組長度的區域,因為普通Java對象通過元數據可以確定大小,而數組的元數據無法確定數組大小。
2.1 Mark Word 的不同狀態下存儲不同內容。
狀態 | 標誌位 | 存儲內容 |
未鎖定 | 01 | 對象哈希碼。對象分代年齡 |
輕量級鎖定 | 00 | 指向鎖記錄的指針 |
膨脹(重量級鎖定) | 10 | 指向重量級鎖的指針 |
GC標記 | 11 | 空,不需要記錄信息 |
可偏向 | 01 | 偏向線程ID、偏向時間戳、對象分代年齡 |
2.2 不同狀態下不同信息所占的位數和位置
3.輕量級鎖的實現和加鎖過程。
- 程序並不是一遇到同步代碼塊立刻就拿到對象的重量級鎖。
-
加鎖
- 代碼進入同步塊時如果鎖對象的標誌位時01那麽虛擬機會在當前線程的棧幀中建立一個 鎖記錄 空間 Lock Record,用來存儲鎖對象目前的Mark Word 的拷貝。
- 然後執行CAS 操作 ,它會比較鎖對象的Mark Word 與拷貝是否相等。如果相等執行3,如果不等執行5
- 如果相等將鎖對象的Mark Word 中的存儲內容替換為 指向棧幀中拷貝的指針,此時這個線程就獲得了該對象的鎖,並且對象的鎖標誌位改為00。
- 此時棧幀中的Mark Word 存儲的內容是(對象的hashCode,分代年齡,偏向鎖位)而鎖對象Mark Word 存儲的內容是(指向棧中鎖記錄的指針),而且前者鎖標誌位01,後者鎖標誌位00.
- 如果不相等首先檢查鎖對象Mark Word內容是否指向當前線程,如果指向執行6,如果沒有指向執行7
- 如果指向說明當前線程已經拿到了鎖,則可以進入同步代碼塊執行,比如同步方法中調用同步方法,並且使用的同一個鎖對象。
- 如果沒有指向當前線程說明鎖已經被別的線程拿到了,此時鎖標誌位改為10,鎖對象Mark Word內容換為指向互斥量(重量級鎖synchronized)的指針,然後執行8
- 註意此時線程發現拿不到鎖也不會立刻被掛起,它會加入自旋,如果自旋一定次數失敗就會進入阻塞狀態。
-
解鎖
- 首先查看鎖對象的Mark Word內容是否指向棧,如果是那麽就交換兩者,同步代碼執行完成。
- 如果沒有,說明有別的線程來拿過鎖,所以要解除互斥量同時喚醒被掛起的線程。
- synchronized是不公平的不會按先後掛起順序喚醒。
4.偏向鎖
- -XX:+UseBiasedLocking 啟用偏向鎖,1.6默認
- 鎖對象第一次被線程獲得,會把標誌位設為01,此時線程ID還是空,對象還未鎖定
- 通過一次CAS操作會把獲取到這個鎖對象的線程ID存入鎖對象的Mark Word。此時對象已被鎖定。
- 之後持有偏向鎖的線程進入同步代碼就不需要任何同步操作了。
- 如果另一個線程來獲取鎖,那麽偏向模式就結束,分別如圖按2種不同情況作出不同反應。
JVM鎖優化