深入學(xué)習(xí)JVM【2】HotSpot虛擬機(jī)對(duì)象探秘

虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)域描述了虛擬機(jī)管理的內(nèi)存劃分情況墙基,但是目前我們對(duì)于虛擬機(jī)還是有很多困惑,比如:

  • 問題1:如何為對(duì)象分配內(nèi)存刷喜?
  • 問題2:對(duì)象內(nèi)存模型是怎樣的残制?
  • 問題3:是怎樣訪問內(nèi)存中的對(duì)象的?
  • 問題4:分配內(nèi)存的時(shí)候如果遇到并發(fā)問題掖疮,怎么保證分配操作的線程安全性初茶?

為了搞清楚這些問題,我們先從虛擬機(jī)是如何創(chuàng)建對(duì)象開始講起浊闪。

一恼布、對(duì)象創(chuàng)建過程

當(dāng)虛擬機(jī)遇到一條new 指令時(shí),便會(huì)進(jìn)行對(duì)象的創(chuàng)建過程搁宾。

創(chuàng)建對(duì)象的過程如下:

  1. 檢查常量池中有沒有這個(gè)類的符號(hào)引用折汞,并且檢查這個(gè)符號(hào)引用代表的類有沒有被虛擬機(jī)加載過。

如果沒有被加載過盖腿,則執(zhí)行類加載過程爽待,然后進(jìn)入下一步;
如果已加載奸忽,則進(jìn)入下一步堕伪。

  1. 根據(jù)方法區(qū)中類的信息,在堆區(qū)劃分一塊確定大小的內(nèi)存給對(duì)象栗菜。

(經(jīng)過類加載后欠雌,類的信息被保存在方法區(qū)中,一個(gè)類的對(duì)象所需的內(nèi)存大小也固定下來疙筹。)

  1. 為對(duì)象的成員變量賦初始值

內(nèi)存分配完成之后富俄,需要對(duì)分配的內(nèi)存空間部分區(qū)域的內(nèi)容都初始化為零值。
這一步保證了對(duì)象成員變量在java代碼中可以不賦初始值而咆。

  1. 設(shè)置對(duì)象頭中的信息
    關(guān)于對(duì)象頭是什么, 別急霍比,繼續(xù)往下看。
  2. 調(diào)用<init>方法進(jìn)行初始化
    別再問<init>是什么了,先往下看暴备。

二悠瞬、問題1解惑:

在堆區(qū)分配內(nèi)存有兩種方式。

  • 指針碰撞法

如果堆中內(nèi)存是規(guī)整的,即所有用過的內(nèi)存都放在一邊浅妆,空閑的內(nèi)存放在另一邊望迎,中間用一個(gè)指針做分界點(diǎn)的指示器。

分配內(nèi)存的過程凌外,實(shí)際上就是指針向空閑空間那邊移動(dòng)一段與對(duì)象大小相等的距離辩尊。

  • 空閑列表法

java堆中的內(nèi)存如果不是規(guī)整的,就需要使用空閑列表的分配方式康辑。

空閑列表概念:虛擬機(jī)維護(hù)了一個(gè)列表摄欲,用于記錄哪些內(nèi)存塊是可用的。

在分配的時(shí)候疮薇,從列表中找到一塊滿足對(duì)象大小的內(nèi)存空間劃分給對(duì)象實(shí)例胸墙,同時(shí)會(huì)更新列表上的記錄

  • 關(guān)于兩種分配方式的選擇

選擇哪種分配方式取決于java堆是否規(guī)整惦辛。

而java堆是否規(guī)整取決于所采用的垃圾收集器是否帶有壓縮整理的功能劳秋。

因此,選擇哪種分配方式最終取決于使用了哪種垃圾收集器胖齐。

  • 使用了指針碰撞的垃圾收集器有哪些玻淑?

serial、ParNew等基于復(fù)制算法或標(biāo)記整理(Mark Compact)算法的收集器呀伙,不會(huì)導(dǎo)致內(nèi)存碎片补履,因此使用的是指針碰撞。

  • 采用空閑鏈表垃圾收集器有哪些剿另?

CMS等基于Mark-Sweep(標(biāo)記清除)算法的收集器箫锤,會(huì)產(chǎn)生內(nèi)存碎片,所以使用空閑列表法雨女。

三谚攒、問題2解惑

對(duì)象在內(nèi)存中的數(shù)據(jù)除了實(shí)例本身的數(shù)據(jù)外,還包括對(duì)象頭對(duì)齊填充

3.1 實(shí)例數(shù)據(jù)

實(shí)例數(shù)據(jù)存儲(chǔ)的是成員變量的值氛堕,包括從父類繼承下來的成員變量馏臭。

成員變量在內(nèi)存中的順序:相同寬度的字段會(huì)分配在一起,父類定義的變量會(huì)出現(xiàn)在子類之前讼稚,
默認(rèn)情況下括儒,子類中較窄的變量可能會(huì)被插入到父類變量的間隙中。反正就是不一定按定義的順序來分配锐想。

3.2 對(duì)象頭是什么帮寻?

對(duì)象頭的作用是記錄對(duì)象在運(yùn)行過程中所需的數(shù)據(jù)

比如對(duì)象屬于哪個(gè)類的實(shí)例赠摇、所屬類的信息在方法區(qū)中的位置(類型指針)固逗、對(duì)象的哈希碼浅蚪、對(duì)象的GC分代年齡等信息。這些信息就保存在對(duì)象頭中(Object Header)

3.3 對(duì)齊填充又是什么抒蚜?

對(duì)齊填充是用于確保對(duì)象的內(nèi)存的總長(zhǎng)度為8字節(jié)的整數(shù)倍掘鄙。

為什么要是確保是8字節(jié)的整數(shù)倍呢?

因?yàn)閔otspot要求對(duì)象起始地址為8字節(jié)的整數(shù)倍以便于自動(dòng)內(nèi)存管理嗡髓,
換句話說,對(duì)象的總長(zhǎng)度要為8字節(jié)的整數(shù)倍才能保證如此收津。
而又因?yàn)閷?duì)象頭正好是8字節(jié)(32位或64位)的整數(shù)倍饿这,但是實(shí)例數(shù)據(jù)長(zhǎng)度是任意的,因此需要對(duì)齊補(bǔ)充來確保整個(gè)對(duì)象總長(zhǎng)度為8字節(jié)的整數(shù)倍撞秋。

四长捧、問題3解惑

java程序需要通過引用來操作堆上的具體數(shù)據(jù)。
根據(jù)引用存放的地址類型的不同吻贿,對(duì)象有不同的訪問方式

主要有兩種訪問方式:

  • 使用句柄訪問
  • 使用直接指針訪問

4.1 使用句柄訪問

圖片來源https://blog.csdn.net/java2000_wl/article/details/8015105

堆中會(huì)劃分一塊內(nèi)存用來做句柄池串结。引用中存儲(chǔ)的就是對(duì)象的句柄地址。句柄包含了對(duì)象實(shí)例數(shù)據(jù)和對(duì)象類型的數(shù)據(jù)的指針舅列。

通過引用訪問對(duì)象的時(shí)候肌割,會(huì)首先根據(jù)引用找到對(duì)象的句柄,然后根據(jù)句柄中對(duì)象的地址來訪問對(duì)象帐要。

4.2 使用直接指針訪問

圖片來源https://blog.csdn.net/java2000_wl/article/details/8015105

引用中存儲(chǔ)的直接是對(duì)象的地址把敞,直接通過引用來訪問對(duì)象。

4.3 兩種方式對(duì)比

  • 使用句柄

    • 優(yōu)點(diǎn):引用中存儲(chǔ)的是穩(wěn)定的句柄地址榨惠,發(fā)生垃圾收集時(shí)可能會(huì)移動(dòng)對(duì)象奋早,這時(shí)候只需要改變句柄中實(shí)例數(shù)據(jù)的指針指向新對(duì)象,而引用的值不需要改赠橙。
    • 缺點(diǎn):需要兩次尋址耽装。
  • 使用直接指針

    • 優(yōu)點(diǎn):速度快,一次尋址即可期揪。
    • 缺點(diǎn):需要在對(duì)象實(shí)例的內(nèi)存中保存一個(gè)指向方法區(qū)中該類型數(shù)據(jù)的指針掉奄。不過使用句柄方式句柄中也需要保存類型指針。

直接指針的速度快横侦,hotspot采用就是直接指針的方式

五挥萌、問題4解惑

對(duì)象分配內(nèi)存不是線程安全的,比如給對(duì)象A分配內(nèi)存枉侧,還沒來得及修改指針的指向引瀑,
另一個(gè)線程創(chuàng)建對(duì)象B也用了原來的指針,這樣就會(huì)出問題的榨馁。

如何解決憨栽?

  • 方案1: 對(duì)分配內(nèi)存空間的動(dòng)作進(jìn)行同步處理

實(shí)際上虛擬機(jī)采用CAS配上失敗重試的方式保證更新指針操作的原子性。

  • 方案2:把內(nèi)存分配的動(dòng)作按照線程劃分在不同的空間中進(jìn)行

即:每個(gè)線程在java堆中預(yù)分配一小塊內(nèi)存,
這一小塊內(nèi)存稱作“本地線程分配緩沖"(Thread Local Allocation Buffer, TLAB)

內(nèi)存分配的過程就可以總結(jié)為:不同線程使用指針碰撞或者空閑列表的方式在各自的TLAB上分配內(nèi)存屑柔。

當(dāng)線程的TLAB用完需要分配新的TLAB屡萤,這時(shí)候才需要同步內(nèi)存分配操作。

虛擬機(jī)是否需要使用TLAB掸宛,可以通過-XX:+/-UseTLAB參數(shù)來決定死陆。

六、遺留問題:<init>方法是個(gè)啥唧瘾?

從上面對(duì)象的創(chuàng)建過程措译,我們可以了解到,在內(nèi)存分配完成之后饰序,所有成員變量的值都還只是零值领虹。

對(duì)于虛擬機(jī)來說,對(duì)象創(chuàng)建已經(jīng)完畢求豫,但是塌衰,對(duì)于java程序來說,對(duì)象的初始化才剛開始蝠嘉。

成員變量的初始化工作交由<init>方法的來完成最疆。

編譯器收集了成員變量上的賦值操作,實(shí)例初始化代碼塊的賦值操作是晨,以及構(gòu)造方法中的賦值操作肚菠,構(gòu)成了<init>方法,并執(zhí)行罩缴,對(duì)象就得到了初始化蚊逢。

學(xué)習(xí)過java基礎(chǔ)的人都知道,初始化的順序?yàn)? 成員變量上的賦值-->實(shí)例初始化塊-->構(gòu)造方法箫章。

<init>方法就解釋了為什么是這個(gè)過程烙荷。

七、講點(diǎn)對(duì)象頭

對(duì)象頭的內(nèi)存模型分三部分:

  • Mark Word
  • 類型指針
  • 記錄數(shù)組的長(zhǎng)度

7.1 Mark Word

存放hashCode檬寂、GC分代年齡终抽、鎖狀態(tài)標(biāo)志、線程持有的鎖桶至、偏向線程id昼伴、偏向時(shí)間戳等。
長(zhǎng)度為32位或者64位(32位虛擬機(jī)和64位虛擬機(jī))镣屹。

mark word是一個(gè)非固定的數(shù)據(jù)結(jié)構(gòu)圃郊,在不同情況下結(jié)構(gòu)會(huì)有所變化。

比如:在32位的虛擬機(jī)中女蜈,如果對(duì)象處于未被鎖定的狀態(tài)持舆,
mark Word的32位空間將有25位用于存儲(chǔ)hashcode,
4位用于存儲(chǔ)對(duì)象的分代年齡色瘩,2位用于存儲(chǔ)對(duì)象上鎖
標(biāo)志,1位固定為0

這些東西我就不一個(gè)個(gè)介紹他們是用來干嘛的逸寓,講多了反而復(fù)雜居兆,大概了解就行,有興趣的可以百度竹伸。

7.2 類型指針

一個(gè)指向類元數(shù)據(jù)的指針泥栖,通過這個(gè)指針,可以確定對(duì)象是哪個(gè)類的實(shí)例佩伤。記住聊倔,這個(gè)指針是在對(duì)象頭中,但不是在Mark Word中的生巡。

7.3 數(shù)組長(zhǎng)度

如果對(duì)象是一個(gè)數(shù)組,在對(duì)象頭中還必須有一塊用于記錄數(shù)組長(zhǎng)度的數(shù)據(jù)见妒。

這一部分僅在對(duì)象是數(shù)組的時(shí)候存在孤荣。

點(diǎn)贊是對(duì)我最大的鼓勵(lì)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市须揣,隨后出現(xiàn)的幾起案子盐股,更是在濱河造成了極大的恐慌,老刑警劉巖耻卡,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疯汁,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡卵酪,警方通過查閱死者的電腦和手機(jī)幌蚊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來溃卡,“玉大人溢豆,你說我怎么就攤上這事∪诚郏” “怎么了漩仙?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)犹赖。 經(jīng)常有香客問我队他,道長(zhǎng),這世上最難降的妖魔是什么峻村? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任麸折,我火速辦了婚禮,結(jié)果婚禮上雀哨,老公的妹妹穿的比我還像新娘磕谅。我一直安慰自己私爷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布膊夹。 她就那樣靜靜地躺著衬浑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪放刨。 梳的紋絲不亂的頭發(fā)上工秩,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音进统,去河邊找鬼助币。 笑死,一個(gè)胖子當(dāng)著我的面吹牛螟碎,可吹牛的內(nèi)容都是我干的眉菱。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼掉分,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼俭缓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起酥郭,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤华坦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后不从,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惜姐,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年椿息,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了歹袁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡撵颊,死狀恐怖宇攻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情倡勇,我是刑警寧澤逞刷,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站妻熊,受9級(jí)特大地震影響夸浅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扔役,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一帆喇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧亿胸,春花似錦坯钦、人聲如沸预皇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吟温。三九已至,卻和暖如春突颊,著一層夾襖步出監(jiān)牢的瞬間鲁豪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工律秃, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爬橡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓棒动,卻偏偏與公主長(zhǎng)得像糙申,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子船惨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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

  • 一郭宝、運(yùn)行時(shí)數(shù)據(jù)區(qū)域 Java虛擬機(jī)管理的內(nèi)存包括幾個(gè)運(yùn)行時(shí)數(shù)據(jù)內(nèi)存:方法區(qū)、虛擬機(jī)棧掷漱、本地方法棧、堆榄檬、程序計(jì)數(shù)器卜范,...
    加油小杜閱讀 1,514評(píng)論 1 15
  • 1.1 概述 Java優(yōu)點(diǎn): 1、結(jié)構(gòu)嚴(yán)謹(jǐn)鹿榜,面向?qū)ο?2海雪、擺脫硬件平臺(tái)束縛,實(shí)現(xiàn)了“一次編寫舱殿,到處運(yùn)行”的理想; ...
    viciyforever閱讀 1,157評(píng)論 1 9
  • 1. Java 內(nèi)存區(qū)域與內(nèi)存溢出異常 1.1 運(yùn)行時(shí)數(shù)據(jù)區(qū)域 根據(jù)《Java 虛擬機(jī)規(guī)范(Java SE 7 版...
    java大濕兄閱讀 639評(píng)論 0 20
  • 趕稿趕到大半夜奥裸,發(fā)現(xiàn)沒有醒著的人了,恰逢奶奶失眠沪袭,我就和她聊了二十塊錢的湾宙。 我奶奶是超人,爺爺當(dāng)年被批斗的時(shí)候冈绊,她...
    caidoi閱讀 368評(píng)論 6 1
  • 我家那位十足是個(gè)木訥之人侠鳄,我都弄不明白,多少帶著文藝范的我為何屁顛屁顛跟著他去取證死宣。這可是我的一輩子呀…… 細(xì)細(xì)想...
    吻塵閱讀 505評(píng)論 44 20