java对象头_我的并发编程(二):java对象头以及synchronized升级过程
一、概述
研究java對象頭的目的是詳細分析Java的synchronized鎖的升級過程,因為synchronized在鎖升級的時候,就是依賴對象頭的信息來決定的。本博文針對64位的操作系統來對Java對象頭進行詳解。
二、詳細分析
1. 用戶態與內核態
內核態與用戶態是操作系統的兩種運行級別,當程序運行在3級特權級上時, 就可以稱之為運行在用戶態,因為這是最低特權級,是普通的用戶進程運 行的特權級,大部分用戶直接面對的程序都是運行在用戶態;當程序運行在0級特權級上時,就可以稱之為運行在內核態。運行在用戶態下的程序不能直接訪問操作系統內核數據結構和程序。當我們在系統中執行一個程序時,大部分時間是運行在用戶態下的,在其需要操作系統幫助完成某些它沒有權力和能力完成的工作時就會切換到內核態。
操作系統對于用戶態線程(也叫纖程)的管理是由系統內核kernel來管理的。JVM中的線程與操作系統的原生線程對應關系一般是1:1的關系。對應關系除了1:1,也有m:1,也就是用戶態的m個線程只對應內核態中的1個線程;m:n的關系,出現在go語言的協程中。計算機上的程序包括JVM是運行在計算機的用戶空間上,運行在用戶空間上的程序的特點就是,在進行一些敏感操作(比如網絡讀寫、讀寫硬盤、內存映射等)的時候,需要通過工作在內核空間的系統內核kernel進行系統調用,這樣的目的是使得操作系統更加的健壯。
synchronized什么是重量級瑣?申請鎖資源的時候需要通過操作系統內核kernel進行系統調用,這個調用過程將會由用戶空間切換到內核空間,就是重量級瑣;輕量級鎖是只在用戶空間就完成對用戶空間鎖的調度管理,直接生成匯編指令,不需要經過系統內核。jdk1.5推出JUC包,利用CAS采用的輕量級鎖自旋鎖來替代一些需要鎖的地方,極大的提高效率。
2. CAS操作原理
CAS全拼又叫做compareAndSwap,從名字上的意思就知道是比較交換的意思。它包含 3 個參數 CAS(V,E,N),V表示要更新變量的值,E表示預期值,N表示新值。僅當 V值等于E值時,才會將V的值設為N,如果V值和E值不同,則說明已經有其他線程做兩個更新,則當前線程則什么都不做。最后,CAS 返回當前V的真實值。隱藏的問題以及解決辦法參考前文《我的jdk源碼(二十四):AtomicInteger類和CAS機制》。
在linux_x86這個具體系統平臺的代碼中源碼如下:
由兩個紅框的內容可以看到,底層的實現是CPU指令原語lock和cmpxchg組合執行的,值得一提的是cmpxchg指令雖然支持CAS,卻并不是原子性的操作,所以我們看下LOCK_IF_MP是怎么操作的,實現的源碼如下:
MP是multi processor的縮寫,意為多處理器。所以在多核處理時,lock指令會鎖住系統總線,進而鎖定一個北橋信號。java中鎖底層基本都是利用lock匯編指令完成。
3. JOL工具
(1) Java Object Layout,引入jar包,就可以在代碼中觀察跟蹤synchronized的瑣升級過程。
(2) 代碼實現如下:
(3) 打印結果如下:
通過上面可以清晰的看到一個對象在內存之中,有哪些值,由哪些東西構成。
(4) 對象在內存中的存儲布局:
mackword:存放瑣標志位、偏向鎖位、線程ID、分代年齡等內容,下面詳細解讀。
Klassword:也就是calss pointer,存儲對象所屬類的地址,就是為了標記到底是什么類的實例。
instance data:保存成員變量實例對象。
padding:是為了保證整個內容加起來能被8個字節(Byte)整除而填充的空間,JVM讀數據是一塊一塊讀的,這樣做效率是最高的。
length:如果對象是數組,需要存儲數組長度。
(5) 根據觀察到的對象頭結構如下:
詳細解釋:
(1) 當我們創建一個無鎖態對象的時候:25位沒有用;31位裝的identity Hashcode,但是只有在被調用的時候,才填充,沒有調用的時候是空的;1位沒有使用的;4位分代年齡(解釋在下面);1位偏向鎖位;2位鎖標志位。
(2) 偏向鎖的時候:54位存下當前線程的ID;2位存批量撤銷Epoch;1位沒有使用;4位分代年齡;1位偏向鎖位;2位鎖標志位。
(3) 自旋鎖:62位指向線程中的Lock Record的指針。Lock Record與鎖重入有關,synchronize默認是可重入的。自旋鎖在競爭鎖的時候,會在自己的內存中創建一個Lock Record對象,搶到鎖對象的資源時,鎖對象頭存的就是這個線程的Lock Record對象的指針,所以在重入的時候,會再創建一個Lock Record對象,利用Lock Record來記錄到底瑣了多少次。解鎖的時候,就將一個Lock Record移除。
(4) 重量級瑣:重量級瑣是在C++代碼層面進行的,會生成一個ObjectMonitor對象,這個對象中記錄了一系列的隊列,如下圖:
(5) 分代年齡:JVM有10種垃圾回收器,前面7種都涉及分代年齡,采用分代算法。當我們創建一個對象的時候,把它放在年輕代中,每經過一次垃圾回收后年齡就+1,也就是垃圾回收無法回收掉這個對象,它的年齡就會不斷的增長,到達15,因為4個字節最大為15,就轉到老齡代,年輕代的回收就不再對它進行回收。
4. synchronized瑣升級過程
注意:
(1) 瑣升級路線:new -> 偏向鎖 -> 輕量級瑣(自旋鎖、自適應自旋鎖) -> 重量級瑣
(2) 如果偏向鎖沒有啟動,那么new出來的對象是普通對象;如果偏向鎖已經啟動,那么new出來的對象就是匿名偏向對象。
(3) 偏向鎖什么情況下轉為輕量級瑣呢?只要有2個線程競爭同一個瑣資源,所有線程都升級為輕量級瑣,也就是都會自旋搶占瑣資源。
(4) 自旋鎖在競爭資源的時候,也是CAS操作,用CAS的方式修改瑣對象的mackword。
(5) 自旋鎖升級為重量級瑣需要符合什么條件呢?jdk1.6以前,某個線程自旋次數上限達到默認的10次,會升級為重量級鎖,因為一直自旋消耗CPU資源;自旋線程數量達到了默認是系統的CPU核數的1/2的時候,全部升級為重量級瑣進入等待隊列。jdk1.6之后,jdk提供了自適應自旋,jdk根據每個線程的運行情況來判斷是否需要升級。
5. 查看設置的偏向鎖參數
JVM參數分為3類:
第一類: 以-開頭的,叫做標準參數,所有的JVM版本都支持,如下圖:
第二類:以-X開頭的,叫做非標準參數,是有版本差異的,有的版本支持,有的版本不支持。
第三類:以-XX開頭的,是可以配置的參數。
利用以下命令,打印所有可以設置參數的項和值打印出來:
利用以下命令,可以查看一共有多少行:
利用以下命令,可以查看所有和偏向鎖有關系的參數:
上圖解釋我們在創建一個對象的時候,即使默認啟用了偏向鎖,創建出來的對象也是無瑣的,但是如果我們在創建對象前先設置5秒的延遲,再創建出來的對象就是偏向鎖狀態的,也就是匿名偏向鎖,也就是上面瑣升級過程圖中的另一種情況。具體操作如下:
代碼如下:
運行結果如下:
三、總結
本文主要介紹了java對象頭的組成與synchronized升級過程,深入底層了解原理,很多細節在文中,需要各位仔細研讀!
更多精彩內容,敬請掃描下方二維碼,關注我的微信公眾號【Java覺淺】,獲取第一時間更新哦!
http://weixin.qq.com/r/xx3v9_7EY7McraqU90jV (二維碼自動識別)
總結
以上是生活随笔為你收集整理的java对象头_我的并发编程(二):java对象头以及synchronized升级过程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab菲涅尔衍射_有问必答——SY
- 下一篇: 如何备份服务器日志到其他服务器_KIWI