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