前言-對象頭結(jié)構(gòu)
java對象頭由3部分組成:
1、Mark Word
2腺逛、指向類對象(對象的class對象)的指針
3穗椅、數(shù)組長度(數(shù)組類型才有)
重點是 Mark Word結(jié)構(gòu),下面以32位HotSpot為例:
一扒秸、偏向鎖
1昨登、概念:
HotSpot的作者經(jīng)過研究發(fā)現(xiàn)趾代,大多數(shù)情況下,鎖不僅不存在多線程競爭丰辣,而且總是由同一線程多次獲得撒强,為了讓線程獲得鎖的代價更低從而引入偏向鎖。偏向鎖在獲取資源的時候會在鎖對象頭上記錄當(dāng)前線程ID笙什,偏向鎖并不會主動釋放飘哨,這樣每次偏向鎖進(jìn)入的時候都會判斷鎖對象頭中線程ID是否為自己,如果是則不需要進(jìn)行額外的操作得湘,直接進(jìn)入同步操作杖玲。
2顿仇、偏向鎖的獲取過程:
I:判斷是否為可偏向狀態(tài)--MarkWord中鎖標(biāo)志是否為‘01’淘正,是否偏向鎖是否為‘1’
II:如果是可偏向狀態(tài)摆马,則查看線程ID是否為當(dāng)前線程,如果是鸿吆,則進(jìn)入步驟'V'囤采,否則進(jìn)入步驟‘III’
III:通過CAS操作競爭鎖,如果競爭成功惩淳,則將MarkWord中線程ID設(shè)置為當(dāng)前線程ID蕉毯,然后執(zhí)行‘V’;競爭失敗思犁,則執(zhí)行‘IV’
IV:CAS獲取偏向鎖失敗表示有競爭代虾。當(dāng)達(dá)到safepoint時獲得偏向鎖的線程被掛起,偏向鎖升級為輕量級鎖激蹲,然后被阻塞在安全點的線程繼續(xù)往下執(zhí)行同步代碼塊
V:執(zhí)行同步代碼
3棉磨、偏向鎖的撤銷過程:
I:偏向鎖不會主動釋放(撤銷),只有遇到其他線程競爭時才會執(zhí)行撤銷学辱,由于撤銷需要知道當(dāng)前持有該偏向鎖的線程棧狀態(tài)乘瓤,因此要等到safepoint時執(zhí)行,此時持有該偏向鎖的線程(T)有‘II’策泣,‘III’兩種情況衙傀;
II:撤銷----T線程已經(jīng)退出同步代碼塊,或者已經(jīng)不再存活萨咕,則直接撤銷偏向鎖统抬,變成無鎖狀態(tài)----該狀態(tài)達(dá)到閾值20則執(zhí)行批量重偏向
III:升級----T線程還在同步代碼塊中,則將T線程的偏向鎖升級為輕量級鎖危队,當(dāng)前線程執(zhí)行輕量級鎖狀態(tài)下的鎖獲取步驟----該狀態(tài)達(dá)到閾值40則執(zhí)行批量撤銷
4蓄喇、批量重偏向/撤銷:
從‘3、偏向鎖的撤銷過程’可以看出偏向鎖需要等到safepoint才能進(jìn)行鎖升級/撤銷交掏,這種情況偏向鎖不僅不能提高性能妆偏,反而會導(dǎo)致性能下降
思考兩個場景:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? I:線程T1創(chuàng)建了大量對象,并進(jìn)行初始的同步操作盅弛,這時這些對象都偏向T1钱骂;之后線程T2使用這些對象作為鎖進(jìn)行同步操作,則會存在大量的偏向鎖撤銷操作? ? ? ? ? ? ? ? ? ? ? ? II:存在明顯多線程鎖競爭時會存在大量偏向鎖升級過程?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 為了解決這兩個問題挪鹏,jvm提供了批量重偏向/撤銷的機制?
批量重偏向/撤銷過程:??
I:首先引入一個概念epoch见秽,其本質(zhì)是一個時間戳,代表了偏向鎖的有效性讨盒,epoch存儲在可偏向?qū)ο蟮腗arkWord中解取。除了對象中的epoch,對象所屬的類class信息中,也會保存一個epoch值
II:每當(dāng)遇到一個全局安全點時(這里的意思是說批量重偏向沒有完全替代了全局安全點返顺,全局安全點是一直存在的)禀苦,比如要對class C 進(jìn)行批量再偏向蔓肯,則首先對 class C中保存的epoch進(jìn)行增加操作,得到一個新的epoch_new
III:然后掃描所有持有 class C 實例的線程棧振乏,根據(jù)線程棧的信息判斷出該線程是否鎖定了該對象蔗包,僅將epoch_new的值賦給被鎖定的對象中,也就是現(xiàn)在偏向鎖還在被使用的對象才會被賦值epoch_new
IV:退出安全點后慧邮,當(dāng)有線程需要嘗試獲取偏向鎖時调限,直接檢查 class C 中存儲的 epoch 值是否與目標(biāo)對象中存儲的 epoch 值相等, 如果不相等误澳,則說明該對象的偏向鎖已經(jīng)無效了(即用完了耻矮,因為'III'步驟里面已經(jīng)說了只有偏向鎖還在被使用的對象才會有epoch_new,這里不相等的原因是class C里面的epoch值是epoch_new,而當(dāng)前對象的epoch里面的值還是epoch)忆谓,此時競爭線程可以嘗試對此對象重新進(jìn)行偏向操作淘钟,即通過CAS操作將其Mark Word的Thread Id 改成當(dāng)前線程Id
V:當(dāng)epoch達(dá)到重偏向閾值(默認(rèn)20)時,jvm就認(rèn)為該class的偏向鎖偏向的線程有問題陪毡,因此會進(jìn)行批量重偏向米母。當(dāng)epoch達(dá)到批量撤銷閾值(默認(rèn)40)時,jvm就認(rèn)為這個class不再適合偏向鎖毡琉,就會批量撤銷铁瞒,并且在之后的加鎖過程中直接為該class的對象設(shè)置輕量級鎖
二、輕量級鎖
1桅滋、概念:
輕量級鎖是相對于重量級鎖需要阻塞/喚醒涉及上下文切換而言慧耍,主要針對多個線程在不同時間請求同一把鎖的場景。
2丐谋、輕量級鎖獲取過程:
I:進(jìn)行加鎖操作時芍碧,jvm會判斷是否已經(jīng)時重量級鎖,如果不是号俐,則會在當(dāng)前線程棧幀中劃出一塊空間泌豆,作為該鎖的鎖記錄,并且將鎖對象MarkWord復(fù)制到該鎖記錄中
II:復(fù)制成功之后吏饿,jvm使用CAS操作將對象頭MarkWord更新為指向鎖記錄的指針踪危,并將鎖記錄里的owner指針指向?qū)ο箢^的MarkWord。如果成功猪落,則執(zhí)行‘III’贞远,否則執(zhí)行‘IV’
III:更新成功,則當(dāng)前線程持有該對象鎖笨忌,并且對象MarkWord鎖標(biāo)志設(shè)置為‘00’蓝仲,即表示此對象處于輕量級鎖狀態(tài)
IV:更新失敗,jvm先檢查對象MarkWord是否指向當(dāng)前線程棧幀中的鎖記錄,如果是則執(zhí)行‘V’袱结,否則執(zhí)行‘VI’
V:表示鎖重入亮隙;然后當(dāng)前線程棧幀中增加一個鎖記錄第一部分(Displaced Mark Word)為null,并指向Mark Word的鎖對象擎勘,起到一個重入計數(shù)器的作用。
VI:表示該鎖對象已經(jīng)被其他線程搶占颖榜,則進(jìn)行自旋等待(默認(rèn)10次)棚饵,等待次數(shù)達(dá)到閾值仍未獲取到鎖,則升級為重量級鎖
3掩完、輕量級鎖解鎖過程:
I:通過CAS操作把嘗試把線程棧幀中復(fù)制的鎖記錄中的Displaced Mark Word替換當(dāng)前對象頭的MarkWord(即還原對象頭)
II:替換成功則同步塊執(zhí)行順利結(jié)束
III:替換失敗說明已經(jīng)膨脹為重量級鎖噪漾,則在執(zhí)行完同步塊釋放鎖同時喚醒被掛起的線程
4、自適應(yīng)自旋:
根據(jù)以往自旋等待時是否能夠獲得鎖且蓬,來動態(tài)調(diào)整自旋的時間(循環(huán)數(shù)目)
三欣硼、重量級鎖
1、重量級鎖加鎖過程:
I:調(diào)用omAlloc分配一個ObjectMonitor對象恶阴,把Mark Word鎖標(biāo)志置為‘10’诈胜,然后Mark Word存儲指向ObjectMonitor對象的指針。ObjectMonitor對象有兩個隊列和一個指針冯事,每個需要獲取鎖的線程都包裝成ObjectWaiter對象
II:多個線程同時執(zhí)行同一段同步代碼時焦匈,ObjectWaiter先進(jìn)入_EntryList隊列,當(dāng)某個線程獲取到對象的monitor以后進(jìn)入_Owner區(qū)域昵仅,并把monitor中的owner變量設(shè)置為當(dāng)前線程同時monitor中的計數(shù)器count+1缓熟;
2、重量級鎖釋放過程:
I:若同步塊中的線程調(diào)用wait()方法摔笤,則釋放持有的monitor够滑,owner遍歷置為null,count-1吕世,同時線程進(jìn)入_WaitSet等待被喚醒
II:若當(dāng)前同步塊執(zhí)行完畢彰触,則也釋放持有的monitor,owner遍歷置為null命辖,count-1