面試官問我:Object o = new Object() 占用了多少個(gè)字節(jié)?
小小面試一下

前言
???今天本文的內(nèi)容就針對(duì)剛剛模擬面試兩個(gè)問題
???1.對(duì)象的創(chuàng)建過程
???2.對(duì)象的內(nèi)存布局
對(duì)象的創(chuàng)建過程
以下內(nèi)容基于HotSpot VM 分代模型

這張圖其實(shí)就能完整的說明一個(gè)對(duì)象的創(chuàng)建過程到底發(fā)生了什么,很多朋友可能一下看不懂,那么我們就跟著左上角的一步一步來:
一個(gè)對(duì)象new出來先判斷線程棧是否能分配下 如果能分配下,直接分配在棧中。 如果分配不下則進(jìn)行第二步。 判斷該對(duì)象是否足夠大 如果足夠大,則直接進(jìn)入老年代。 如果不夠大,則進(jìn)行第三步。 判斷創(chuàng)建對(duì)象的線程的TLAB(本地線程緩沖區(qū))空間是否足夠 如果足夠,直接分配在TLAB中。 如果不夠,則進(jìn)入Eden區(qū)中其他空間。然后進(jìn)行第四步。 GC清除 如果清除掉了該對(duì)象,則直接結(jié)束。 如果沒有清除掉對(duì)象,進(jìn)行第5步。 此刻對(duì)象進(jìn)入Survivor 1 區(qū),判斷年齡是否足夠大 如果年齡足夠大,則直接進(jìn)入old區(qū)域。 如果年齡不夠大,則進(jìn)入Survivor 2 區(qū),然后進(jìn)入第4步,循環(huán)往復(fù)。
???通過這張流程圖和步驟解析大家應(yīng)該對(duì)一個(gè)對(duì)象的創(chuàng)建過程有一個(gè)很清晰的概念了,但是其中還是有很多小細(xì)節(jié)會(huì)被忽略,為什么jvm會(huì)在對(duì)象的創(chuàng)建過程中大作文章,會(huì)分這么多種情況?為了讓大家更深入的能夠理解它,我們就再來看看下面這幾個(gè)問題:
為什么對(duì)象會(huì)選擇先分配在棧中?
???首先棧是線程私有的,將對(duì)象優(yōu)先分配在棧中,可以通過pop直接將對(duì)象的所有信息,空間直接清除,當(dāng)線程消亡的時(shí)候也可以直接清理這一塊兒TLAB區(qū)域。
為什么jvm會(huì)讓大對(duì)象會(huì)直接進(jìn)入老年代?
???大對(duì)象需要連續(xù)的空間來存儲(chǔ),如果不存入老年代對(duì)jvm說就可能是一個(gè)負(fù)擔(dān),如果沒有足夠的空間就有可能導(dǎo)致提前觸發(fā)gc來清理空間來安置大對(duì)象。
為什么會(huì)選擇先進(jìn)入TLAB?
???TLAB是線程本地緩沖區(qū),TLAB的好處就是防止不同線程創(chuàng)建對(duì)象選擇同一塊兒內(nèi)存區(qū)域而產(chǎn)生競(jìng)爭(zhēng),會(huì)使其概率大大減少。
為什么會(huì)有兩個(gè)Survivor區(qū)?并且存活且年齡不夠大的對(duì)象會(huì)從一個(gè)Survivor區(qū)轉(zhuǎn)到另一個(gè)Survivor區(qū)?
???根據(jù)根可達(dá)算法,jvm會(huì)從開始尋找到所有正在使用的對(duì)象,沒有使用的就是垃圾,通常情況下,很多對(duì)象都是用完就拋棄的,所以真正在Survivor區(qū)長(zhǎng)時(shí)間存活的對(duì)象非常少,將這部分對(duì)象從一個(gè)Survivor區(qū)轉(zhuǎn)到另一個(gè)Survivor區(qū)后,就可以直接對(duì)這個(gè)Survivor區(qū)進(jìn)行全量的空間回收了,效率會(huì)很高。
對(duì)象的內(nèi)存布局
???作者可不是標(biāo)題黨,哈哈,所以我們回到文章的標(biāo)題,Object o = new Object();到底占用多少個(gè)字節(jié)?這道題的目的其實(shí)就是考驗(yàn)看你對(duì)對(duì)象的內(nèi)存布局了解的是否清晰,先上圖:

???在java中對(duì)象的內(nèi)存布局分為兩種情況,非數(shù)組對(duì)象和數(shù)組對(duì)象,數(shù)組對(duì)象和非數(shù)組對(duì)象的區(qū)別就是需要額外的空間存儲(chǔ)數(shù)組的長(zhǎng)度length。
對(duì)象頭
???對(duì)象頭又分為MarkWord和Class Pointer兩部分。
MarkWord:包含一系列的標(biāo)記位,比如輕量級(jí)鎖的標(biāo)記位,偏向鎖標(biāo)記位,gc記錄信息等等,在32位系統(tǒng)占4字節(jié),在64位系統(tǒng)中占8字節(jié)。
ClassPointer:用來指向?qū)ο髮?duì)應(yīng)的Class對(duì)象(其對(duì)應(yīng)的元數(shù)據(jù)對(duì)象)的內(nèi)存地址。在32位系統(tǒng)占4字節(jié),在64位系統(tǒng)中占8字節(jié)。
Length:只在數(shù)組對(duì)象中存在,用來記錄數(shù)組的長(zhǎng)度,占用4字節(jié)
Interface data
Interface data:對(duì)象實(shí)際數(shù)據(jù),對(duì)象實(shí)際數(shù)據(jù)包括了對(duì)象的所有成員變量,其大小由各個(gè)成員變量的大小決定。(這里不包括靜態(tài)成員變量,因?yàn)槠涫窃诜椒▍^(qū)維護(hù)的)
Padding
Padding:Java對(duì)象占用空間是8字節(jié)對(duì)齊的,即所有Java對(duì)象占用bytes數(shù)必須是8的倍數(shù),是因?yàn)楫?dāng)我們從磁盤中取一個(gè)數(shù)據(jù)時(shí),不會(huì)說我想取一個(gè)字節(jié)就是一個(gè)字節(jié),都是按照一塊兒一塊兒來取的,這一塊大小是8個(gè)字節(jié),所以為了完整,padding的作用就是補(bǔ)充字節(jié),保證對(duì)象是8字節(jié)的整數(shù)倍。
???moon在上文特意標(biāo)注了32位系統(tǒng)和64位系統(tǒng)不同區(qū)域占用空間大小的區(qū)別,這是因?yàn)?strong>對(duì)象指針在64位JVM下的尋址更長(zhǎng),所以想比32位會(huì)多出來更多占用空間。
???但是現(xiàn)在假設(shè)一個(gè)場(chǎng)景,公司現(xiàn)在項(xiàng)目部署的機(jī)器是32位的,你們老板要讓你將項(xiàng)目遷移到64位的系統(tǒng)上,但是又因?yàn)?4位系統(tǒng)比32位系統(tǒng)要多出更多占用空間,怎么辦,因?yàn)檎碚f我們是不需要這一部分多余空間的,所以jvm已經(jīng)幫你考慮好了,那就是指針壓縮。
指針壓縮
???-XX:+UseCompressedOops 這個(gè)參數(shù)就是JVM提供給你的解決方案,可以壓縮指針,將占用的空間壓縮為原來的一半,起到節(jié)約空間的作用,classpointer參數(shù)大小就受到其影響。
Object o = new Object()到底占用多少個(gè)字節(jié)?
???通過剛才內(nèi)存布局的學(xué)習(xí)后,這個(gè)問題就很好回答了,面試官其實(shí)就是想問你對(duì)象的內(nèi)存布局是怎樣的,我們這里就針對(duì)這個(gè)問題的結(jié)果分析下,這里分兩種情況:
在開啟指針壓縮的情況下,markword占用8字節(jié),classpoint占用4字節(jié),Interface data無數(shù)據(jù),總共是12字節(jié),由于對(duì)象需要為8的整數(shù)倍,Padding會(huì)補(bǔ)充4個(gè)字節(jié),總共占用16字節(jié)的存儲(chǔ)空間。
在沒有指針的情況下,markword占用8字節(jié),classpoint占用8字節(jié),Interface data無數(shù)據(jù),總共是16字節(jié)。
結(jié)語
???今天的文章和大家介紹了一個(gè)對(duì)象的創(chuàng)建過程,從它的出生到死亡,都經(jīng)歷了什么?也和大家詳細(xì)的說明了對(duì)象的內(nèi)存布局,深入解剖了一下對(duì)象的身體構(gòu)造,這下面試官再問你,可就有的聊了,這篇文章的內(nèi)容還是比較肝的,需要大家認(rèn)真閱讀一下 。
推薦閱讀:
最近面試BAT,整理一份面試資料《Java面試BAT通關(guān)手冊(cè)》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù)?666?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
明天見(??ω??)??
