漲姿勢啦!Java程序員裝X必備詞彙之對象標記Mark Word!

大家好,我是慶哥Java,一個專注於乾貨分享的Java自學者!

寫在前面

如果你已經知道什麼是Mark Word,那我也希望你都好好閱讀下本篇文章,因爲你有可能發現不一樣的切入點來幫助你更加深入的瞭解Mark Word,這對你來說是個很好的鞏固所學知識的機會,同時也是一場技術交流,一個有逼格的程序員應該不會錯過這樣的機會吧!

如果你還不知道什麼是Mark Word,那你更要好好閱讀本篇文章了,因爲Mark Word不僅是一個可以讓你用來裝X的詞彙,實際上它是一個非常重要的概念,非常重要的知識點,對你學習Java中的各種鎖是非常有必要的,也可以說是必須的,而爲什麼要閱讀本篇文章嘞?

因爲,慶哥的文章,接地氣啊,通俗易懂,那咱就一起搞起吧!


先來看下Mark Word

Mark Word是啥?翻譯過來就是對象標記,先通過代碼讓你直觀看下Mark Word到底是個什麼東東,來看下面代碼:

class OneClass{
    
}

這是啥?一個非常非常簡單的類,啥也沒有,就是聲明瞭一個類對吧,好,看接下來的操作:

public static void main(String[] args) {
        System.out.println(ClassLayout.parseInstance(new OneClass()).toPrintable());
}

這是啥?先來看看打印出來的是些個啥?
image.png
看得懂嗎?紅框中的就是對象標記Mark Word了,那今天這篇文章的目的就是讓你讀懂上面這張圖到底是個啥?以及有哪些重要的信息!

事先聲明,今天的文章,乾貨比較多,看起來有點費勁,請先做好準備!

開幹!


從對象實例開始講起

什麼是對象實例呢?說的簡單點,我們new出來的東西就是一個對象實例,也就是平常說的什麼實例化,就是你創建出來的那個在堆裏面的實例對象,ok,這個概念相信大家都懂,不贅述,接下來你就有可能不知道了,你說對於一個對象實例,它有哪幾部分組成呢?

  1. 對象頭
  2. 實例數據
  3. 對齊填充

這些都是啥?這裏我們可以類比下我們的人,一個對象實例就好比是一個完整的人,拿你自己來類比就行,這裏我們拿你自己來類比,這個對象頭就是你的大腦啦,然後實例數據就好比的身體,而對齊填充就好比你的腳,畫個圖大概就是這樣的:
image.png
這裏可能這個對齊填充大家不是很好理解,什麼意思呢?就比如說你要參加一個面試,但是人家硬性要求一米八,但是你就是一米七八,咋辦,鞋墊子拯救你啊,懂我意思吧。 那這個對齊填充也是這麼回事,對齊填充要求一個對象實例的大小必須是8個字節的倍數,那你不夠了的話,對齊填充起作用給你整到8字節的倍數,懂了吧!

現在我們知道了,一個對象實例包括對象頭,實例數據和對齊填充,其實更詳細的還有如下劃分,看圖:
image.png
什麼意思呢?就是你得知道這麼一回事,對於對象頭來說,它是分爲兩部分的,一是對象標記,也就是今天要注重說的Mark Word,還有一個就是Class Pointer類型指針了,咱們的重點是Mark Word!

實例數據和對齊填充

這裏先給大家簡單說說什麼是實例數據,幫助大家有個直觀的認識!

什麼叫做實例數據呢?寫一個簡單的代碼來舉例說明:
image.png

我們這裏定義了一個超級簡單的Person類,裏面啥也沒有,這個時候我們去實例化一個Person對象,也就是這樣:
image.png

這個時候你知道我們這個對象有多大嗎?實際上目前它就佔16個字節,也就是對象頭中的Mark Word和類型指針,說白了就是,你剛開始創建一個空蕩蕩的Java對象,起初裏面是隻有對象頭的,也就是組成對象頭的對象標記Mark Word佔8個字節,類型指針佔8個字節(存在指針壓縮的話就只有4個字節,會對齊填充4個字節),一共16個字節,如果這個時候我們再添加一點代碼,比如這樣:
image.png

我們給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存在哪裏,它是在方法區的,結合上面說的對象標記,來看一張圖:

image.png
一定要注意這個Person的位置在哪裏!它是存在方法區的,這個是java虛擬機的知識了,先作簡單瞭解即可!

而這裏的類型指針,就是指向這個Person類的指針了!那麼關於什麼是類型指針,你是否也有一個簡單的認識了?不再是一個陌生的詞彙了吧!

這就ok啦,咱們繼續,重難點還在後面嘞!


詳解Mark Word

對於一個Java對象來說,對象頭是極其重要的,對象頭主要有對象標記和類型指針兩部分組成,接下來需要重點看下對象標記,也就是Mark Word,下面是針對64位JVM(那也就是說還有32位的,考慮到現在基本都是64的了,32的直接pass掉)的Mark Word來說的,它的組成是這樣的:
image.png
第一次看到這張圖會覺得比較懵,沒事,很正常,我剛開始學習也是這樣,彆着急,經過下面的一步步的分析,你就會清晰很多!首先記住,上圖表示的就是一個Mark Word的內部是啥樣的!我們之前不是說了嘛,對象標記Mark Word是佔8字節的,那也就是64bit,那這64bit都是存儲的啥,上面這種圖就給出了答案,你看這裏:

image.png

一個Mark Word就是8字節64位,裏面存儲了啥,就得看上面這張圖,有內味兒了吧,上面這張圖是不是稍微熟悉了一點點,那現在你應該知道這麼些東西:

一個Java對象由對象頭,實例數據和對齊填充組成,其中對象頭是極其重要的,對象頭是由對象標記Mark Word和類型指針組成,其中又以Mark Word最重要,對於Mark Word它佔8個字節也就是64位!

ok,以上內容都清楚吧!那咱就繼續!

接下來咱就以鎖這個切入點去詳細的看看這個Mark Word!

我們都知道在併發情況下也就是要解決線程安全的話要加鎖,其實這個加鎖就是對對象加鎖,那如何去判斷或者說知道這個對象加鎖了沒有或者加的什麼鎖,這些個信息其實就保存在對象頭中的Mark Word中,上面這張圖就是關於一個Mark Word的具體結構,可以看出,一個Java對象其實在不同的狀態下,它是不一樣的,主要是有不同的鎖,比如沒有鎖,輕量級鎖,還有什麼偏向鎖,重量級鎖等等,不同的鎖,在Mark Word中就會有不同的狀態標記。

JOL的引入

只看這些,其實是比較晦澀難懂的,接下來需要結合代碼來看下:

image.png

這就是一個空類,啥也沒有,要怎麼看這個對象標記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,接下來加入上述這個依賴之後我們就可以這樣操作,看代碼:

image.png

這樣我們就可以看出使用虛擬機的一些情況,看打印輸出:
image.png
接着我們就來看下我們創建的那個空類是怎樣的一個情況,可以這樣操作:

MyClass myClass = new MyClass();
//打印出相關的對象頭信息
System.out.println(ClassLayout.parseInstance(myClass).toPrintable());

得出以下內容:
image.png

OK了,相關信息打印出來了,記住這是myclass的相關內部信息,那這些都是啥呢?看上面的圖,是不是有個“Object header”啥意思?不就是對象頭嘛,然後我們來看這裏:

image.png

這是啥?主要就是來看我們的一個空對象佔多大空間,之前也說了,對象頭包括對象標記8字節和類型指針8字節,我們先來看對象標記,是不是這個:

image.png

也就是起始位置是0,然後前進4個字節,此時的起始位置就變成了4,接着再前進4個字節,那此時起始位置就成了8,此時對象標記就佔8個字節,這沒啥問題,接着我們看,起始位置是8,然後繼續前進4字節,到達12的時候,其實此時類型指針就結束了,也就是8到12,類型指針就佔了4字節,看這句話:
image.png

也就是此時這個對象就佔據12個字節,可是不是說佔據16個字節纔對嗎?我們知道java對象所佔大小需要是8字節的整數倍,那爲了滿足這塊,就又對齊填充了4字節,最終達到16字節 。

爲什麼會這樣?其實是因爲這裏發生了指針壓縮,在說這個指針壓縮之前,需要明白這樣的一東西:
image.png

我們通過代碼來說下:
image.png

我們在空類中加入一個char類型,再看下結果:

image.png

接着我們再看一個:
image.png

我就問此時你說會進行對齊填充幾個字節?思考一下:

image.png

爲什麼是6,一定要想明白了,記住8的倍數哦!對了,一個對象有多大,記得可以看這裏:

image.png

指針壓縮

接着我們再說之前指針壓縮的問題,也就是類型指針是佔8個字節的,可是實際上只佔了4個字節,這就是指針壓縮的問題,這裏我們需要這麼一個jvm參數,就是它:

java -XX:+PrintCommandLineFlags -version

我們執行看下:
image.png

也就是默認開啓了指針壓縮,所以我們的類型指針才變成了4字節,接着我們可以把這個指針壓縮給關閉掉,可以這樣操作:
image.png

再看:
image.png

此時就正確啦,就是對象標記佔8個字節,類型指針也佔8個字節,也就是對象頭16個字節。

我們之前說過,一個java對象包括對象頭,實例數據和對齊填充,經過我們上面的講解,然後在看下面一張圖,我相信你可以秒懂:
image.png

有沒有一種豁然開朗的感覺?到了這裏就需要分析最後一波,就是value值了,也就是這部分值(注意我們重點在Mark Word的value值上):

image.png

value值分析

那這裏需要再看看一下這張非常重要的圖了:
image.png
然後再來看我們這裏創建的類:
image.png

這就是一個普通的類,沒有加鎖,也就是最普通的無鎖,我們再看它的對象結構是啥:

image.png

我們主要來看value這部分,這裏我們仍然需要對照Mark Word的結構來看,這裏是無鎖結構,所以我們需要看的是這裏:

image.png
注意重點等下我們需要看下這個鎖標誌位,然後下面看一個對照圖:

一張圖理解Mark Word

image.png

要仔細看這張圖了,懂了這個,基本上你就掌握了Mark Word!爲了讓你更加的清晰,再給你來一張圖:
image.png

總結

對於一個Java對象而言,Mark Word是相當重要的,而且它是你後續學習併發編程,研究Java中的各種鎖需要必會的知識,剛開始可能覺得是個新奇陌生的詞彙,但是等你慢慢的去接觸它。瞭解它,你會發現它真的能幫你解決很多看起來很難的知識,會讓你覺得那些看起來很高大上的東西,原來是這麼回事啊,比如偏向鎖,輕量級鎖什麼的,總之,作爲一個Java程序員,Mark Word是你需要要掌握的!

當然,由於個人水平有限,本篇描述可能存在有誤的地方,煩請指正,大家共同學習,一起進步!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章