(轉(zhuǎn)載)深入理解多線(xiàn)程(二)—— Java的對(duì)象模型

原文鏈接:深入理解多線(xiàn)程(二)—— Java的對(duì)象模型-HollisChuang's Blog

上一篇文章中簡(jiǎn)單介紹過(guò)synchronized關(guān)鍵字的方式恒序,其中瘦麸,同步代碼塊使用monitorenter和monitorexit兩個(gè)指令實(shí)現(xiàn),同步方法使用ACC_SYNCHRONIZED標(biāo)記符實(shí)現(xiàn)歧胁。后面幾篇文章會(huì)從JVM源碼的角度更加深入瞎暑,層層剝開(kāi)synchronized的面紗。

在進(jìn)入正題之前与帆,肯定有些基礎(chǔ)知識(shí)需要鋪墊了赌,那么先來(lái)看一下一個(gè)容易被忽略的但是又很重要的知識(shí)點(diǎn) —— Java對(duì)象模型 。

大家都知道的是玄糟,Java對(duì)象保存在堆內(nèi)存中勿她。在內(nèi)存中,一個(gè)Java對(duì)象包含三部分:對(duì)象頭阵翎、實(shí)例數(shù)據(jù)和對(duì)齊填充逢并。其中對(duì)象頭是一個(gè)很關(guān)鍵的部分之剧,因?yàn)?b>對(duì)象頭中包含鎖狀態(tài)標(biāo)志、線(xiàn)程持有的鎖等標(biāo)志砍聊。這篇文章就主要從Java對(duì)象模型入手背稼,找一找我們關(guān)系的對(duì)象頭以及對(duì)象頭中和鎖相關(guān)的運(yùn)行時(shí)數(shù)據(jù)在JVM中是如何表示的。

Java的對(duì)象模型

任何一個(gè)接觸過(guò)Java的人都知道玻蝌,Java是一種面向?qū)ο笳Z(yǔ)言蟹肘。在學(xué)習(xí)Java的過(guò)程中你一定對(duì)下面兩句話(huà)不陌生:

1、在面向?qū)ο蟮能浖懈┦鳎瑢?duì)象(Object)是某一個(gè)類(lèi)(Class)的實(shí)例帘腹。?維基百科

2、一切皆對(duì)象?Thinking In Java

我們還知道许饿,在JVM的內(nèi)存結(jié)構(gòu)中阳欲,對(duì)象保存在堆內(nèi)存中,而我們?cè)趯?duì)對(duì)象進(jìn)行操作時(shí)陋率,其實(shí)操作的是對(duì)象的引用球化。那么對(duì)象本身在JVM中的結(jié)構(gòu)是什么樣的呢?本文的所有分析均基于HotSpot虛擬機(jī)瓦糟。

oop-klass model

HotSpot是基于c++實(shí)現(xiàn)筒愚,而c++是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,本身是具備面向?qū)ο蠡咎卣鞯睦暌常訨ava中的對(duì)象表示,最簡(jiǎn)單的做法是為每個(gè)Java類(lèi)生成一個(gè)c++類(lèi)與之對(duì)應(yīng)扯再。但HotSpot JVM并沒(méi)有這么做芍耘,而是設(shè)計(jì)了一個(gè)OOP-Klass Model。OOP(Ordinary Object Pointer)指的是普通對(duì)象指針熄阻,而Klass用來(lái)描述對(duì)象實(shí)例的具體類(lèi)型斋竞。

為什么HotSpot要設(shè)計(jì)一套o(hù)op-klass model呢?答案是:HotSopt JVM的設(shè)計(jì)者不想讓每個(gè)對(duì)象中都含有一個(gè)vtable(虛函數(shù)表)

這個(gè)解釋似乎可以說(shuō)得通秃殉。眾所周知坝初,C++和Java都是面向?qū)ο蟮恼Z(yǔ)言,面向?qū)ο笳Z(yǔ)言有一個(gè)很重要的特性就是多態(tài)钾军。關(guān)于多態(tài)的實(shí)現(xiàn)鳄袍,C++和Java有著本質(zhì)的區(qū)別蜀涨。

多態(tài)是面向?qū)ο蟮淖钪饕奶匦灾话段希且环N方法的動(dòng)態(tài)綁定,實(shí)現(xiàn)運(yùn)行時(shí)的類(lèi)型決定對(duì)象的行為努隙。多態(tài)的表現(xiàn)形式是父類(lèi)指針或引用指向子類(lèi)對(duì)象樱哼,在這個(gè)指針上調(diào)用的方法使用子類(lèi)的實(shí)現(xiàn)版本哀九。多態(tài)是IOC剿配、模板模式實(shí)現(xiàn)的關(guān)鍵。

在C++中通過(guò)虛函數(shù)表的方式實(shí)現(xiàn)多態(tài)阅束,每個(gè)包含虛函數(shù)的類(lèi)都具有一個(gè)虛函數(shù)表(virtual table)呼胚,在這個(gè)類(lèi)對(duì)象的地址空間的最靠前的位置存有指向虛函數(shù)表的指針。在虛函數(shù)表中息裸,按照聲明順序依次排列所有的虛函數(shù)蝇更。由于C++在運(yùn)行時(shí)并不維護(hù)類(lèi)型信息,所以在編譯時(shí)直接在子類(lèi)的虛函數(shù)表中將被子類(lèi)重寫(xiě)的方法替換掉界牡。

在Java中簿寂,在運(yùn)行時(shí)會(huì)維持類(lèi)型信息以及類(lèi)的繼承體系。每一個(gè)類(lèi)會(huì)在方法區(qū)中對(duì)應(yīng)一個(gè)數(shù)據(jù)結(jié)構(gòu)用于存放類(lèi)的信息宿亡,可以通過(guò)Class對(duì)象訪問(wèn)這個(gè)數(shù)據(jù)結(jié)構(gòu)常遂。其中,類(lèi)型信息具有superclass屬性指示了其超類(lèi)挽荠,以及這個(gè)類(lèi)對(duì)應(yīng)的方法表(其中只包含這個(gè)類(lèi)定義的方法克胳,不包括從超類(lèi)繼承來(lái)的)。而每一個(gè)在堆上創(chuàng)建的對(duì)象圈匆,都具有一個(gè)指向方法區(qū)類(lèi)型信息數(shù)據(jù)結(jié)構(gòu)的指針漠另,通過(guò)這個(gè)指針可以確定對(duì)象的類(lèi)型。

上面這段是我從網(wǎng)上摘取過(guò)來(lái)的跃赚,說(shuō)的有一定道理笆搓,但是也不全對(duì)。至于為啥纬傲,我會(huì)在后文介紹到Klass的時(shí)候細(xì)說(shuō)满败。

關(guān)于opp-klass模型的整體定義,在HotSpot的源碼中可以找到叹括。

oops模塊可以分成兩個(gè)相對(duì)獨(dú)立的部分:OOP框架和Klass框架算墨。

oopsHierarchy.hpp里定義了oop和klass各自的體系。

oop-klass結(jié)構(gòu)

oop體系:

上面列出的是整個(gè)Oops模塊的組成結(jié)構(gòu)汁雷,其中包含多個(gè)子模塊净嘀。每一個(gè)子模塊對(duì)應(yīng)一個(gè)類(lèi)型,每一個(gè)類(lèi)型的OOP都代表一個(gè)在JVM內(nèi)部使用的特定對(duì)象的類(lèi)型侠讯。

從上面的代碼中可以看到挖藏,有一個(gè)變量opp的類(lèi)型是oppDesc?,OOPS類(lèi)的共同基類(lèi)型為oopDesc厢漩。

在Java程序運(yùn)行過(guò)程中熬苍,每創(chuàng)建一個(gè)新的對(duì)象,在JVM內(nèi)部就會(huì)相應(yīng)地創(chuàng)建一個(gè)對(duì)應(yīng)類(lèi)型的OOP對(duì)象。在HotSpot中柴底,根據(jù)JVM內(nèi)部使用的對(duì)象業(yè)務(wù)類(lèi)型婿脸,具有多種oopDesc的子類(lèi)。除了oppDesc類(lèi)型外柄驻,opp體系中還有很多instanceOopDesc狐树、arrayOopDesc?等類(lèi)型的實(shí)例,他們都是oopDesc的子類(lèi)鸿脓。

這些OOPS在JVM內(nèi)部有著不同的用途抑钟,例如,instanceOopDesc表示類(lèi)實(shí)例野哭,arrayOopDesc表示數(shù)組在塔。也就是說(shuō),當(dāng)我們使用new創(chuàng)建一個(gè)Java對(duì)象實(shí)例的時(shí)候拨黔,JVM會(huì)創(chuàng)建一個(gè)instanceOopDesc對(duì)象來(lái)表示這個(gè)Java對(duì)象蛔溃。同理,當(dāng)我們使用new創(chuàng)建一個(gè)Java數(shù)組實(shí)例的時(shí)候篱蝇,JVM會(huì)創(chuàng)建一個(gè)arrayOopDesc對(duì)象來(lái)表示這個(gè)數(shù)組對(duì)象贺待。

在HotSpot中,oopDesc類(lèi)定義在oop.hpp中零截,instanceOopDesc定義在instanceOop.hpp中麸塞,arrayOopDesc定義在arrayOop.hpp中。

簡(jiǎn)單看一下相關(guān)定義:

通過(guò)上面的源碼可以看到涧衙,instanceOopDesc實(shí)際上就是繼承了oopDesc哪工,并沒(méi)有增加其他的數(shù)據(jù)結(jié)構(gòu),也就是說(shuō)instanceOopDesc中主要包含以下幾部分?jǐn)?shù)據(jù):markOop _mark和union _metadata?以及一些不同類(lèi)型的?field弧哎。

HotSpot虛擬機(jī)中雁比,對(duì)象在內(nèi)存中存儲(chǔ)的布局可以分為三塊區(qū)域:對(duì)象頭、實(shí)例數(shù)據(jù)和對(duì)齊填充傻铣。在虛擬機(jī)內(nèi)部章贞,一個(gè)Java對(duì)象對(duì)應(yīng)一個(gè)instanceOopDesc的對(duì)象祥绞。其中對(duì)象頭包含了兩部分內(nèi)容:_mark和_metadata非洲,而實(shí)例數(shù)據(jù)則保存在oopDesc中定義的各種field中。

_mark

文章開(kāi)頭我們就說(shuō)過(guò)蜕径,之所以我們要寫(xiě)這篇文章两踏,是因?yàn)閷?duì)象頭中有和鎖相關(guān)的運(yùn)行時(shí)數(shù)據(jù),這些運(yùn)行時(shí)數(shù)據(jù)是synchronized以及其他類(lèi)型的鎖實(shí)現(xiàn)的重要基礎(chǔ)兜喻,而關(guān)于鎖標(biāo)記梦染、GC分代等信息均保存在_mark中。因?yàn)楸疚闹饕榻B的oop-klass模型,在這里暫時(shí)不對(duì)對(duì)象頭做展開(kāi)帕识,下一篇文章介紹泛粹。

_metadata

前面介紹到的_metadata是一個(gè)共用體,其中_klass是普通指針肮疗,_compressed_klass是壓縮類(lèi)指針晶姊。在深入介紹之前,就要來(lái)到oop-Klass中的另外一個(gè)主角klass了伪货。

klass

klass體系

和oopDesc是其他oop類(lèi)型的父類(lèi)一樣们衙,Klass類(lèi)是其他klass類(lèi)型的父類(lèi)。

Klass向JVM提供兩個(gè)功能:

實(shí)現(xiàn)語(yǔ)言層面的Java類(lèi)(在Klass基類(lèi)中已經(jīng)實(shí)現(xiàn))

實(shí)現(xiàn)Java對(duì)象的分發(fā)功能(由Klass的子類(lèi)提供虛函數(shù)實(shí)現(xiàn))

文章開(kāi)頭的時(shí)候說(shuō)過(guò):之所以設(shè)計(jì)oop-klass模型碱呼,是因?yàn)镠otSopt JVM的設(shè)計(jì)者不想讓每個(gè)對(duì)象中都含有一個(gè)虛函數(shù)表蒙挑。

HotSopt JVM的設(shè)計(jì)者把對(duì)象一拆為二,分為klass和oop愚臀,其中oop的職能主要在于表示對(duì)象的實(shí)例數(shù)據(jù)忆蚀,所以其中不含有任何虛函數(shù)。而klass為了實(shí)現(xiàn)虛函數(shù)多態(tài)懊悯,所以提供了虛函數(shù)表蜓谋。所以,關(guān)于Java的多態(tài)炭分,其實(shí)也有虛函數(shù)的影子在桃焕。

_metadata是一個(gè)共用體,其中_klass是普通指針捧毛,_compressed_klass是壓縮類(lèi)指針观堂。這兩個(gè)指針都指向instanceKlass對(duì)象,它用來(lái)描述對(duì)象的具體類(lèi)型呀忧。

instanceKlass

JVM在運(yùn)行時(shí)师痕,需要一種用來(lái)標(biāo)識(shí)Java內(nèi)部類(lèi)型的機(jī)制。在HotSpot中的解決方案是:為每一個(gè)已加載的Java類(lèi)創(chuàng)建一個(gè)instanceKlass對(duì)象而账,用來(lái)在JVM層表示Java類(lèi)胰坟。

來(lái)看下instanceKlass的內(nèi)部結(jié)構(gòu):

可以看到,一個(gè)類(lèi)該具有的東西泞辐,這里面基本都包含了笔横。

這里還有個(gè)點(diǎn)需要簡(jiǎn)單介紹一下。

在JVM中咐吼,對(duì)象在內(nèi)存中的基本存在形式就是oop吹缔。那么,對(duì)象所屬的類(lèi)锯茄,在JVM中也是一種對(duì)象厢塘,因此它們實(shí)際上也會(huì)被組織成一種oop茶没,即klassOop。同樣的晚碾,對(duì)于klassOop抓半,也有對(duì)應(yīng)的一個(gè)klass來(lái)描述,它就是klassKlass格嘁,也是klass的一個(gè)子類(lèi)琅关。klassKlass作為oop的klass鏈的端點(diǎn)。關(guān)于對(duì)象和數(shù)組的klass鏈大致如下圖:

在這種設(shè)計(jì)下讥蔽,JVM對(duì)內(nèi)存的分配和回收涣易,都可以采用統(tǒng)一的方式來(lái)管理。oop-klass-klassKlass關(guān)系如圖:

內(nèi)存存儲(chǔ)

關(guān)于一個(gè)Java對(duì)象冶伞,他的存儲(chǔ)是怎樣的新症,一般很多人會(huì)回答:對(duì)象存儲(chǔ)在堆上。稍微好一點(diǎn)的人會(huì)回答:對(duì)象存儲(chǔ)在堆上响禽,對(duì)象的引用存儲(chǔ)在棧上徒爹。今天,再給你一個(gè)更加顯得牛逼的回答:

對(duì)象的實(shí)例(instantOopDesc)保存在堆上芋类,對(duì)象的元數(shù)據(jù)(instantKlass)保存在方法區(qū)隆嗅,對(duì)象的引用保存在棧上。

其實(shí)如果細(xì)追究的話(huà)侯繁,上面這句話(huà)有點(diǎn)故意賣(mài)弄的意思胖喳。因?yàn)槲覀兌贾馈?b>方法區(qū)用于存儲(chǔ)虛擬機(jī)加載的類(lèi)信息、常量贮竟、靜態(tài)變量丽焊、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。?所謂加載的類(lèi)信息咕别,其實(shí)不就是給每一個(gè)被加載的類(lèi)都創(chuàng)建了一個(gè) instantKlass對(duì)象么技健。

talk is cheap ,show me the code :

存儲(chǔ)結(jié)構(gòu)如下:

從上圖中可以看到,在方法區(qū)的instantKlass中有一個(gè)int a=1的數(shù)據(jù)存儲(chǔ)惰拱。在堆內(nèi)存中的兩個(gè)對(duì)象的oop中雌贱,分別維護(hù)著int b=3,int b=2的實(shí)例數(shù)據(jù)。和oopDesc一樣偿短,instantKlass也維護(hù)著一些fields欣孤,用來(lái)保存類(lèi)中定義的類(lèi)數(shù)據(jù),比如int a=1翔冀。

總結(jié)

每一個(gè)Java類(lèi)导街,在被JVM加載的時(shí)候披泪,JVM會(huì)給這個(gè)類(lèi)創(chuàng)建一個(gè)instanceKlass纤子,保存在方法區(qū),用來(lái)在JVM層表示該Java類(lèi)。當(dāng)我們?cè)贘ava代碼中控硼,使用new創(chuàng)建一個(gè)對(duì)象的時(shí)候泽论,JVM會(huì)創(chuàng)建一個(gè)instanceOopDesc對(duì)象,這個(gè)對(duì)象中包含了兩部分信息卡乾,對(duì)象頭以及元數(shù)據(jù)翼悴。對(duì)象頭中有一些運(yùn)行時(shí)數(shù)據(jù),其中就包括和多線(xiàn)程相關(guān)的鎖的信息幔妨。元數(shù)據(jù)其實(shí)維護(hù)的是指針鹦赎,指向的是對(duì)象所屬的類(lèi)的instanceKlass。

參考資料

【理解HotSpot虛擬機(jī)】對(duì)象在jvm中的表示:OOP-Klass模型

Java反射: 從JDK到JVM全鏈路詳解

HotSpotVM 對(duì)象機(jī)制實(shí)現(xiàn)淺析#1

HotSpot實(shí)戰(zhàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末误堡,一起剝皮案震驚了整個(gè)濱河市古话,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锁施,老刑警劉巖陪踩,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異悉抵,居然都是意外死亡肩狂,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)姥饰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)傻谁,“玉大人,你說(shuō)我怎么就攤上這事列粪≌っ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵篱竭,是天一觀的道長(zhǎng)力图。 經(jīng)常有香客問(wèn)我,道長(zhǎng)掺逼,這世上最難降的妖魔是什么吃媒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮吕喘,結(jié)果婚禮上赘那,老公的妹妹穿的比我還像新娘。我一直安慰自己氯质,他們只是感情好募舟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著闻察,像睡著了一般拱礁。 火紅的嫁衣襯著肌膚如雪琢锋。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天呢灶,我揣著相機(jī)與錄音吴超,去河邊找鬼。 笑死鸯乃,一個(gè)胖子當(dāng)著我的面吹牛鲸阻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缨睡,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鸟悴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了奖年?” 一聲冷哼從身側(cè)響起遣臼,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拾并,沒(méi)想到半個(gè)月后揍堰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嗅义,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年屏歹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片之碗。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蝙眶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出褪那,到底是詐尸還是另有隱情幽纷,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布博敬,位于F島的核電站友浸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏偏窝。R本人自食惡果不足惜收恢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祭往。 院中可真熱鬧伦意,春花似錦、人聲如沸硼补。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)已骇。三九已至离钝,卻和暖如春票编,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奈辰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乱豆,地道東北人奖恰。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像宛裕,于是被迫代替她去往敵國(guó)和親瑟啃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • oop-klass模型 Hotspot 虛擬機(jī)在內(nèi)部使用兩組類(lèi)來(lái)表示Java的類(lèi)和對(duì)象揩尸。 oop(ordinary...
    CodeKing2017閱讀 3,699評(píng)論 1 6
  • data source : Codecademy Manipulation SQL is a programmin...
    陳昱熹閱讀 270評(píng)論 0 0
  • 我是樊登讀書(shū)會(huì)伏寶震岩榆,這是我的每日一篇文章之124篇错负。我寫(xiě)的每一篇文章都是為了記錄我和孩子們成長(zhǎng)的點(diǎn)點(diǎn)滴滴,...
    伏寶震閱讀 204評(píng)論 0 0
  • 【五】孟懿子問(wèn)孝勇边,子曰:“無(wú)違犹撒。”樊遲御粒褒,子告之曰:“孟孫問(wèn)孝于我识颊,我對(duì)曰‘無(wú)違’∞确兀”樊遲曰:“何謂也祥款?”子曰:“...
    論語(yǔ)心解閱讀 1,280評(píng)論 0 1
  • 方古董來(lái)到外婆的這個(gè)村莊,真的好像穿越到古代的某個(gè)農(nóng)村月杉,村里沒(méi)有電刃跛,沒(méi)有自來(lái)水,沒(méi)有液化氣苛萎。人們都還在過(guò)著相對(duì)原態(tài)...
    風(fēng)燈客閱讀 1,193評(píng)論 0 50