深入理解JVM系列(五)阿里面試題-關(guān)于對(duì)象Object o = new Object()靈魂6問(wèn)

如果覺(jué)得寫(xiě)的還可以請(qǐng)關(guān)注微信公眾號(hào):程序猿的日常分享,定期更新分享直撤。

請(qǐng)解釋一下對(duì)象的創(chuàng)建過(guò)程非竿?

1、加載
2谋竖、鏈接(驗(yàn)證红柱、準(zhǔn)備、解析)
3蓖乘、初始化
4锤悄、申請(qǐng)對(duì)象內(nèi)存
5、成員變量賦默認(rèn)值
6嘉抒、調(diào)用構(gòu)造方法<init>:1)成員變量順序賦初始值 2)執(zhí)行構(gòu)造方法語(yǔ)句

對(duì)象在內(nèi)存中的存儲(chǔ)布局铁蹈?

jvm中的對(duì)象分為兩種,一種是普通對(duì)象众眨,一種是數(shù)組對(duì)象。這兩種對(duì)象在內(nèi)存中的布局是不一樣的容诬。如下圖所示:


image.png

普通對(duì)象new Object()有4部分組成娩梨,分別是對(duì)象頭、類型指針览徒、實(shí)例數(shù)據(jù)狈定、填充。
數(shù)組對(duì)象int i = new int[4]有5部分組成习蓬,分別是對(duì)象頭纽什、類型指針、數(shù)組長(zhǎng)度躲叼、實(shí)例數(shù)據(jù)芦缰、填充。

  • 對(duì)象頭(Mark Word)
    用于存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù)枫慷,如哈希碼(HashCode)让蕾、GC分代年齡、鎖狀態(tài)標(biāo)志或听、線程持有的鎖探孝、偏向線程ID、偏向時(shí)間戳等誉裆,這部分?jǐn)?shù)據(jù)的長(zhǎng)度在32位和64位的虛擬機(jī)中分別為4個(gè)字節(jié)和8個(gè)字節(jié)顿颅,官方稱它為 Mark Word。

  • 類型指針(Class Pointer)
    存儲(chǔ)對(duì)象所屬類的地址足丢,就是為了標(biāo)記到底是什么類的實(shí)例粱腻。jvm默認(rèn)開(kāi)啟了指針壓縮庇配,所以占用4個(gè)字節(jié),如果關(guān)閉指針壓縮栖疑,就占用8個(gè)字節(jié)讨永。此外,指針壓縮還會(huì)影響instance data的實(shí)例對(duì)象的指針空間占用大小遇革。如果開(kāi)啟了指針壓縮卿闹,Long型的成員變量和long型的成員變量占用空間大小是有區(qū)別的:Long占用4個(gè)字節(jié);long是基礎(chǔ)類型占用8個(gè)字節(jié)萝快。如果關(guān)閉了指針壓縮:Long占用8個(gè)字節(jié)锻霎;long是基礎(chǔ)類型占用8個(gè)字節(jié)。
    Hotspot開(kāi)啟內(nèi)存壓縮的規(guī)則(64位機(jī)):
    1揪漩、4G以下旋恼,直接砍掉高32位
    2、4G - 32G奄容,默認(rèn)開(kāi)啟內(nèi)存壓縮 ClassPointers Oops
    3冰更、32G,壓縮無(wú)效昂勒,使用64位蜀细,所以內(nèi)存并不是越大越好

  • 實(shí)例數(shù)據(jù)(Instance Data)
    實(shí)例數(shù)據(jù)部分是對(duì)象真正存儲(chǔ)的有效信息,也是在程序代碼中所定義各種類型的字段內(nèi)容戈盈。無(wú)論是從父類繼承下來(lái)的奠衔,還是在子類中定義的,都需要記錄下來(lái)塘娶。父類定義的變量會(huì)出現(xiàn)在子類定義的變量的前面归斤。各字段的分配策略為longs/doubles、ints刁岸、shorts/chars脏里、bytes/boolean、oops(ordinary object pointers)难捌,相同寬度的字段總是被分配到一起膝宁,便于之后取數(shù)據(jù)。

  • 填充(Padding)
    填充并不是必然存在的根吁,也沒(méi)有特別的含義员淫,它僅僅起著占位符的作用。為什么需要有對(duì)齊填充呢击敌?由于JVM讀數(shù)據(jù)時(shí)是按照一塊一塊的讀取的介返,這樣讀取效率更高,64位虛擬機(jī)的話對(duì)象的大小必須是8字節(jié)的整數(shù)倍。因此圣蝎,當(dāng)對(duì)象實(shí)例數(shù)據(jù)部分沒(méi)有對(duì)齊時(shí)刃宵,就需要通過(guò)對(duì)齊填充來(lái)補(bǔ)全。

  • 數(shù)組長(zhǎng)度(Length)
    存儲(chǔ)了數(shù)組對(duì)象的長(zhǎng)度徘公,占用4個(gè)字節(jié)

對(duì)象頭具體包括什么牲证?

Mark Word的結(jié)構(gòu),定義在markOop.hpp文件中关面,其中定義了32位是怎么實(shí)現(xiàn)的坦袍,64位是怎么實(shí)現(xiàn)的。源碼如下:

image.png

以64位虛擬機(jī)來(lái)看翻譯過(guò)來(lái)以后如下:
image.png

1等太、當(dāng)我們創(chuàng)建一個(gè)無(wú)鎖態(tài)對(duì)象的時(shí)候:25位沒(méi)有用捂齐;31位裝的identity Hashcode,但是只有在被調(diào)用的時(shí)候缩抡,才填充奠宜,沒(méi)有調(diào)用的時(shí)候是空的;1位沒(méi)有使用的瞻想;4位分代年齡(解釋在下面)压真;1位偏向鎖位;2位鎖標(biāo)志位蘑险。
2榴都、偏向鎖的時(shí)候:54位存下當(dāng)前線程的ID;2位存批量撤銷Epoch漠其;1位沒(méi)有使用;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來(lái)記錄到底瑣了多少次枣察。解鎖的時(shí)候,就將一個(gè)Lock Record移除,移除的方式是FILO序目,也就是先進(jìn)后出的原則臂痕。
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)過(guò)一次垃圾回收后年齡就+1昙读,也就是垃圾回收無(wú)法回收掉這個(gè)對(duì)象召调,它的年齡就會(huì)不斷的增長(zhǎng),到達(dá)15蛮浑,因?yàn)?個(gè)字節(jié)最大為15唠叛,就轉(zhuǎn)到老齡代,年輕代的回收就不再對(duì)它進(jìn)行回收沮稚。
6艺沼、hashCode部分:對(duì)象頭上的hashCode并不是我們調(diào)用重寫(xiě)的hashCode()方法生成的,而是為重寫(xiě)的hashCode()方法或者調(diào)用System.identityHashcode()方法才能獲取并且存入對(duì)象頭中蕴掏。通俗來(lái)講障般,這里的hashCode是按照原始內(nèi)容計(jì)算的,重寫(xiě)過(guò)的hashCode()方法計(jì)算的結(jié)果并不會(huì)存在此處盛杰。如果對(duì)象沒(méi)有重寫(xiě)hashCode()方法挽荡,那么默認(rèn)調(diào)用的os::random產(chǎn)生hashCode,也可以通過(guò)System.identityHashcode()獲取即供。os::random產(chǎn)生hashCode的規(guī)則是:next_rand = (16807seed)mod(2*31-1)定拟,因此可以使用31位存儲(chǔ)空間進(jìn)行存儲(chǔ),并且一旦產(chǎn)生這個(gè)hashCode逗嫡,JVM就會(huì)記錄在mark word中青自。

關(guān)于鎖有幾個(gè)需要注意的地方:

1、當(dāng)一個(gè)對(duì)象已經(jīng)計(jì)算過(guò)identity hash code驱证,則它就無(wú)法進(jìn)入偏向鎖狀態(tài)延窜。因?yàn)槿绻呀?jīng)計(jì)算過(guò)identity hash code的值以后,在上圖中偏向鎖記錄線程ID的內(nèi)存已經(jīng)被占用了抹锄。
2需曾、當(dāng)一個(gè)對(duì)象正處于偏向鎖狀態(tài),并且需要計(jì)算identity hash code的話,則它的偏向鎖會(huì)被撤銷呆万、膨脹為重量級(jí)鎖
3商源、重量級(jí)鎖的實(shí)現(xiàn)中,ObjectMonitor類里有字段可以記錄非加鎖狀態(tài)下的mark word谋减,其中可以存儲(chǔ)identity hash code的值牡彻。

對(duì)象怎么定位

JVM中對(duì)象訪問(wèn)定位兩種方式:
1、直接指針訪問(wèn):Java棧直接與對(duì)象進(jìn)行訪問(wèn)出爹,在Java堆中對(duì)象帆布中必須考慮存儲(chǔ)訪問(wèn)類型的數(shù)據(jù)的相關(guān)信息 庄吼,直接指針訪問(wèn)的優(yōu)點(diǎn)比較明顯,就是訪問(wèn)速度快严就,不需要和句柄一樣指針定位的開(kāi)銷 总寻。缺點(diǎn)也比較明顯,就是對(duì)象在GC過(guò)程中梢为,在新生代區(qū)域復(fù)制移動(dòng)時(shí)渐行,會(huì)比較麻煩。如下圖:


image.png

2铸董、通過(guò)句柄池方式訪問(wèn):在Java堆中分出一塊內(nèi)存進(jìn)行存儲(chǔ)句柄池祟印,在棧中存儲(chǔ)的是句柄的地址,通過(guò)句柄池訪問(wèn)有獨(dú)特的優(yōu)點(diǎn)粟害,就是當(dāng)對(duì)象移動(dòng)的時(shí)候(垃圾回收的時(shí)候移動(dòng)很普遍)蕴忆,這樣值需要改變句柄中的指針,但是棧中的指針不需要變化悲幅,因?yàn)闂V写鎯?chǔ)的是句柄的地址套鹅。那么對(duì)應(yīng)的缺點(diǎn)就是需要兩次指針轉(zhuǎn)換進(jìn)行訪問(wèn),訪問(wèn)速度比直接指針訪問(wèn)稍慢一些汰具。如下圖:


image.png

對(duì)象怎么分配

對(duì)象分配流程如下圖:


image.png

1芋哭、當(dāng)我們new出一個(gè)對(duì)象,JVM會(huì)首先嘗試往棧上分配郁副,如果能夠分配得下,就分配到棧上分配到棧上的對(duì)象有好處就是不需要GC進(jìn)行管理豌习,什么時(shí)候不需要用到此對(duì)象了存谎,將對(duì)象出棧就可以了。但是分配到棧上的對(duì)象是有要求的:第一肥隆,對(duì)象比較小既荚,因?yàn)闂?臻g本來(lái)就不夠大栋艳;第二恰聘,對(duì)象比較簡(jiǎn)單。
2、如果棧上分配不下晴叨,我們就判斷這個(gè)對(duì)象是不是夠大凿宾,如果足夠大就直接放在老年代區(qū),在老年代區(qū)的對(duì)象經(jīng)過(guò)一次全量垃圾回收FGC后兼蕊,才有可能被回收掉初厚。
3、如果如果棧上分配不下并且對(duì)象不大孙技,就會(huì)判斷對(duì)象能否被存在線程本地分配緩沖區(qū)-TLAB(Thread Local Allocation Buffer)产禾。但是不管放不放得下,都是放在新生代區(qū)的伊甸區(qū)eden牵啦。 但是因?yàn)槎咽枪蚕淼难乔椋鄠€(gè)線程可以同時(shí)創(chuàng)建對(duì)象就可能會(huì)爭(zhēng)奪同一塊內(nèi)存區(qū)域,所以為了保證線程安全哈雏,Eden區(qū)又被分配成一個(gè)個(gè)線程本地分配緩沖區(qū)楞件,這個(gè)TLAB是線程私有的,每個(gè)線程都有自己的TLAB僧著,避免了多線程環(huán)境下使用同步技術(shù)帶來(lái)的性能損耗履因。
4、伊甸區(qū)eden的對(duì)象在經(jīng)過(guò)一次GC后盹愚,如果被回收掉了栅迄,那就結(jié)束了生命周期。
5皆怕、伊甸區(qū)eden的對(duì)象在經(jīng)過(guò)一次GC后毅舆,如果沒(méi)有被回收掉,會(huì)被拷貝到幸存者區(qū)survivor1愈腾,對(duì)比上面的堆內(nèi)存邏輯分區(qū)圖憋活。幸存者區(qū)survivor1中的對(duì)象再經(jīng)過(guò)一次GC后如果對(duì)象還存活,那么就拷貝到幸存者區(qū)survivor2并且清理掉幸存者區(qū)survivor1中的所有對(duì)象虱黄,再有GC就反復(fù)這個(gè)操作悦即,直到對(duì)象的分代年齡達(dá)到了移到老年代的界限(一般默認(rèn)是15),就會(huì)被移到老年代中橱乱。

Object o = new Object()在內(nèi)存中占用多少字節(jié)辜梳?

這里考察的知識(shí)點(diǎn)是對(duì)象在內(nèi)存中的存儲(chǔ)布局結(jié)構(gòu)和類指針以及普通對(duì)象指針的概念。存儲(chǔ)布局不再多說(shuō)泳叠,類指針就是存儲(chǔ)布局中的class pointer作瞄,普通對(duì)象指針就是存儲(chǔ)布局中的instance data中,成員變量如果不是基礎(chǔ)類型而是引用類型危纫,那么也會(huì)有普通對(duì)象指針指向所屬類宗挥。默認(rèn)情況下JVM是開(kāi)啟了類指針和普通對(duì)象指針的指針壓縮乌庶,將8個(gè)字節(jié)壓縮成了4個(gè)字節(jié)。我們用代碼輸出來(lái)觀察對(duì)象的大小契耿,實(shí)驗(yàn)代碼如下:


image.png

(1) 默認(rèn)開(kāi)啟所有指針壓縮的情況下輸出如下:


image.png

(2) 關(guān)閉類指針壓縮后瞒大,如下:


image.png

如果覺(jué)得寫(xiě)的還可以請(qǐng)關(guān)注微信公眾號(hào):程序猿的日常分享,定期更新分享宵喂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末糠赦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子锅棕,更是在濱河造成了極大的恐慌拙泽,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裸燎,死亡現(xiàn)場(chǎng)離奇詭異顾瞻,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)德绿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)荷荤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人移稳,你說(shuō)我怎么就攤上這事蕴纳。” “怎么了个粱?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵古毛,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我都许,道長(zhǎng)稻薇,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任胶征,我火速辦了婚禮塞椎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘睛低。我一直安慰自己案狠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布钱雷。 她就那樣靜靜地躺著骂铁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪急波。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,475評(píng)論 1 312
  • 那天瘪校,我揣著相機(jī)與錄音澄暮,去河邊找鬼名段。 笑死,一個(gè)胖子當(dāng)著我的面吹牛泣懊,可吹牛的內(nèi)容都是我干的伸辟。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼馍刮,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼信夫!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起卡啰,我...
    開(kāi)封第一講書(shū)人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤静稻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后匈辱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體振湾,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年亡脸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了押搪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡浅碾,死狀恐怖大州,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情垂谢,我是刑警寧澤厦画,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站埂陆,受9級(jí)特大地震影響苛白,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜焚虱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一购裙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鹃栽,春花似錦躏率、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至丰嘉,卻和暖如春夯到,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饮亏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工耍贾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阅爽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓荐开,卻偏偏與公主長(zhǎng)得像付翁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子晃听,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361