若是你已經知道什麼是Mark Word,那我也但願你都好好閱讀下本篇文章,由於你有可能發現不同的切入點來幫助你更加深刻的瞭解Mark Word,這對你來講是個很好的鞏固所學知識的機會,同時也是一場技術交流,一個有逼格的程序員應該不會錯過這樣的機會吧!
java
若是你還不知道什麼是Mark Word,那你更要好好閱讀本篇文章了,由於Mark Word不只是一個可讓你用來裝X的詞彙,實際上它是一個很是重要的概念,很是重要的知識點,對你學習Java中的各類鎖是很是有必要的,也能夠說是必須的,而爲何要閱讀本篇文章嘞?
程序員
由於,慶哥的文章,接地氣啊,通俗易懂,那咱就一塊兒搞起吧!面試
Mark Word是啥?翻譯過來就是對象標記,先經過代碼讓你直觀看下Mark Word究竟是個什麼東東,來看下面代碼:編程
class OneClass{ }
這是啥?一個很是很是簡單的類,啥也沒有,就是聲明瞭一個類對吧,好,看接下來的操做:安全
public static void main(String[] args) { System.out.println(ClassLayout.parseInstance(new OneClass()).toPrintable()); }
這是啥?先來看看打印出來的是些個啥?
看得懂嗎?紅框中的就是對象標記Mark Word了,那今天這篇文章的目的就是讓你讀懂上面這張圖究竟是個啥?以及有哪些重要的信息!
markdown
事先聲明,今天的文章,乾貨比較多,看起來有點費勁,請先作好準備!
併發
開幹!
jvm
什麼是對象實例呢?說的簡單點,咱們new出來的東西就是一個對象實例,也就是日常說的什麼實例化,就是你建立出來的那個在堆裏面的實例對象,ok,這個概念相信你們都懂,不贅述,接下來你就有可能不知道了,你說對於一個對象實例,它有哪幾部分組成呢?
maven
這些都是啥?這裏咱們能夠類比下咱們的人,一個對象實例就比如是一個完整的人,拿你本身來類比就行,這裏咱們拿你本身來類比,這個對象頭就是你的大腦啦,而後實例數據就比如的身體,而對齊填充就比如你的腳,畫個圖大概就是這樣的:
這裏可能這個對齊填充你們不是很好理解,什麼意思呢?就好比說你要參加一個面試,可是人家硬性要求一米八,可是你就是一米七八,咋辦,鞋墊子拯救你啊,懂我意思吧。 那這個對齊填充也是這麼回事,對齊填充要求一個對象實例的大小必須是8個字節的倍數,那你不夠了的話,對齊填充起做用給你整到8字節的倍數,懂了吧!ide
如今咱們知道了,一個對象實例包括對象頭,實例數據和對齊填充,其實更詳細的還有以下劃分,看圖:
什麼意思呢?就是你得知道這麼一回事,對於對象頭來講,它是分爲兩部分的,一是對象標記,也就是今天要注重說的Mark Word,還有一個就是Class Pointer類型指針了,我們的重點是Mark Word!
這裏先給你們簡單說說什麼是實例數據,幫助你們有個直觀的認識!
什麼叫作實例數據呢?寫一個簡單的代碼來舉例說明:
咱們這裏定義了一個超級簡單的Person類,裏面啥也沒有,這個時候咱們去實例化一個Person對象,也就是這樣:
這個時候你知道咱們這個對象有多大嗎?實際上目前它就佔16個字節,也就是對象頭中的Mark Word和類型指針,說白了就是,你剛開始建立一個空蕩蕩的Java對象,起初裏面是隻有對象頭的,也就是組成對象頭的對象標記Mark Word佔8個字節,類型指針佔8個字節(存在指針壓縮的話就只有4個字節,會對齊填充4個字節),一共16個字節,若是這個時候咱們再添加一點代碼,好比這樣:
咱們給Person類增長了一個屬性,咱們知道int佔四個字節,加上對象頭的16個字節,此時一共20個字節,可是一個對象的大小要是8字節的倍數,那目前20字節,因此須要到24字節才符合條件,此時就須要對齊填充4個字節,最終湊成24個字節,這個Person類中的一些東西(好比這裏定義的int)就是實例數據了,另外想必這下你也更清楚什麼是對齊填充了吧!
ok,到了這裏,什麼是實例數據,什麼是對齊填充,是否是比較清晰了?固然若是你目前理解的還不是很透徹也不要緊,只有一個大體知道是怎麼回事就ok啦!
這是個啥意思呢?咱們來看,好比咱們建立一個Person對象,咱們通常怎麼寫,是否是這樣:
Person p = new Person()
別看這一行簡單的代碼,你得分清楚幾個概念了,首先就是new person(),這個你們最熟悉,就是建立一個新的對象,在哪,是在堆中建立吧,而後就是這個p了,它是啥,這個是對象引用,存儲在哪,知道吧,這個是在棧內存,那麼這個Person呢?是否是不少人就不太清楚了呢?
記住了,這個Person能夠理解成一個模板,就比如人類,而後後面new Person()生產出具體的某我的,而這個Person就是個生產出這我的的模具,最重要的要知道,這個Person存在哪裏,它是在方法區的,結合上面說的對象標記,來看一張圖:
必定要注意這個Person的位置在哪裏!它是存在方法區的,這個是java虛擬機的知識了,先做簡單瞭解便可!
而這裏的類型指針,就是指向這個Person類的指針了!那麼關於什麼是類型指針,你是否也有一個簡單的認識了?再也不是一個陌生的詞彙了吧!
這就ok啦,我們繼續,重難點還在後面嘞!
對於一個Java對象來講,對象頭是極其重要的,對象頭主要有對象標記和類型指針兩部分組成,接下來須要重點看下對象標記,也就是Mark Word,下面是針對64位JVM(那也就是說還有32位的,考慮到如今基本都是64的了,32的直接pass掉)的Mark Word來講的,它的組成是這樣的:
第一次看到這張圖會以爲比較懵,沒事,很正常,我剛開始學習也是這樣,彆着急,通過下面的一步步的分析,你就會清晰不少!首先記住,上圖表示的就是一個Mark Word的內部是啥樣的!咱們以前不是說了嘛,對象標記Mark Word是佔8字節的,那也就是64bit,那這64bit都是存儲的啥,上面這種圖就給出了答案,你看這裏:
一個Mark Word就是8字節64位,裏面存儲了啥,就得看上面這張圖,有內味兒了吧,上面這張圖是否是稍微熟悉了一點點,那如今你應該知道這麼些東西:
一個Java對象由對象頭,實例數據和對齊填充組成,其中對象頭是極其重要的,對象頭是由對象標記Mark Word和類型指針組成,其中又以Mark Word最重要,對於Mark Word它佔8個字節也就是64位!
ok,以上內容都清楚吧!那咱就繼續!
接下來咱就以鎖這個切入點去詳細的看看這個Mark Word!
咱們都知道在併發狀況下也就是要解決線程安全的話要加鎖,其實這個加鎖就是對對象加鎖,那如何去判斷或者說知道這個對象加鎖了沒有或者加的什麼鎖,這些個信息其實就保存在對象頭中的Mark Word中,上面這張圖就是關於一個Mark Word的具體結構,能夠看出,一個Java對象其實在不一樣的狀態下,它是不同的,主要是有不一樣的鎖,好比沒有鎖,輕量級鎖,還有什麼偏向鎖,重量級鎖等等,不一樣的鎖,在Mark Word中就會有不一樣的狀態標記。
只看這些,實際上是比較晦澀難懂的,接下來須要結合代碼來看下:
這就是一個空類,啥也沒有,要怎麼看這個對象標記Mark Word呢?這裏咱們須要加入一個依賴,也就是你的Java項目是個maven項目,能夠引入pom依賴,而後添加如下依賴:
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.10</version> </dependency>
這個依賴是幹嗎的呢?這是一個代碼工具叫作JOL,也就是Java Object Layout,主要就是用來分析java虛擬機中的對象佈局的,也就是在java對象在虛擬機中的大小和分佈,ok,接下來加入上述這個依賴以後咱們就能夠這樣操做,看代碼:
這樣咱們就能夠看出使用虛擬機的一些狀況,看打印輸出:
接着咱們就來看下咱們建立的那個空類是怎樣的一個狀況,能夠這樣操做:
MyClass myClass = new MyClass(); //打印出相關的對象頭信息 System.out.println(ClassLayout.parseInstance(myClass).toPrintable());
得出如下內容:
OK了,相關信息打印出來了,記住這是myclass的相關內部信息,那這些都是啥呢?看上面的圖,是否是有個「Object header」啥意思?不就是對象頭嘛,而後咱們來看這裏:
這是啥?主要就是來看咱們的一個空對象佔多大空間,以前也說了,對象頭包括對象標記8字節和類型指針8字節,咱們先來看對象標記,是否是這個:
也就是起始位置是0,而後前進4個字節,此時的起始位置就變成了4,接着再前進4個字節,那此時起始位置就成了8,此時對象標記就佔8個字節,這沒啥問題,接着咱們看,起始位置是8,而後繼續前進4字節,到達12的時候,其實此時類型指針就結束了,也就是8到12,類型指針就佔了4字節,看這句話:
也就是此時這個對象就佔據12個字節,但是不是說佔據16個字節纔對嗎?咱們知道java對象所佔大小須要是8字節的整數倍,那爲了知足這塊,就又對齊填充了4字節,最終達到16字節 。
爲何會這樣?實際上是由於這裏發生了指針壓縮,在說這個指針壓縮以前,須要明白這樣的一東西:
咱們經過代碼來講下:
咱們在空類中加入一個char類型,再看下結果:
接着咱們再看一個:
我就問此時你說會進行對齊填充幾個字節?思考一下:
爲何是6,必定要想明白了,記住8的倍數哦!對了,一個對象有多大,記得能夠看這裏:
接着咱們再說以前指針壓縮的問題,也就是類型指針是佔8個字節的,但是實際上只佔了4個字節,這就是指針壓縮的問題,這裏咱們須要這麼一個jvm參數,就是它:
java -XX:+PrintCommandLineFlags -version
咱們執行看下:
也就是默認開啓了指針壓縮,因此咱們的類型指針才變成了4字節,接着咱們能夠把這個指針壓縮給關閉掉,能夠這樣操做:
再看:
此時就正確啦,就是對象標記佔8個字節,類型指針也佔8個字節,也就是對象頭16個字節。
咱們以前說過,一個java對象包括對象頭,實例數據和對齊填充,通過咱們上面的講解,而後在看下面一張圖,我相信你能夠秒懂:
有沒有一種豁然開朗的感受?到了這裏就須要分析最後一波,就是value值了,也就是這部分值(注意咱們重點在Mark Word的value值上):
那這裏須要再看看一下這張很是重要的圖了:
而後再來看咱們這裏建立的類:
這就是一個普通的類,沒有加鎖,也就是最普通的無鎖,咱們再看它的對象結構是啥:
咱們主要來看value這部分,這裏咱們仍然須要對照Mark Word的結構來看,這裏是無鎖結構,因此咱們須要看的是這裏:
注意重點等下咱們須要看下這個鎖標誌位,而後下面看一個對照圖:
要仔細看這張圖了,懂了這個,基本上你就掌握了Mark Word!爲了讓你更加的清晰,再給你來一張圖:
對於一個Java對象而言,Mark Word是至關重要的,並且它是你後續學習併發編程,研究Java中的各類鎖須要必會的知識,剛開始可能以爲是個新奇陌生的詞彙,可是等你慢慢的去接觸它。瞭解它,你會發現它真的能幫你解決不少看起來很難的知識,會讓你以爲那些看起來很高大上的東西,原來是這麼回事啊,好比偏向鎖,輕量級鎖什麼的,總之,做爲一個Java程序員,Mark Word是你須要要掌握的!
固然,因爲我的水平有限,本片描述可能存在有誤的地方,煩請指正,你們共同窗習,一塊兒進步!