HotSpot DeMYSTiFieD 之 內(nèi)存中的對象

大將生來膽氣豪 腰橫秋水雁翎刀

風(fēng)吹鼉鼓山河動 電閃旌旗日月高

天上麒麟原有種 穴中螻蟻豈能逃

太平待詔歸來日 朕與將軍解戰(zhàn)袍

開篇聊閑天锰瘸。

在使用Java的過程中,一直困擾我的一個問題是,一個對象到底占用多大內(nèi)存?(Java并沒有sizeof操作符) 但這個問題戚哎,結(jié)果卻并沒有那么簡單。

Java沒有sizeof也不需要sizeof操作符掀抹,所有數(shù)據(jù)類型的大小都在Java語言規(guī)范中定義,和機(jī)器平臺不相關(guān)心俗。但從Java虛擬機(jī)的角度傲武,一個Java中定義的對象,在虛擬機(jī)中占用多大內(nèi)存?這個問題好像只能通過分析虛擬機(jī)的實(shí)現(xiàn)來找到答案谱轨。這就牽扯到另一個問題,我們到底需不需要了解我們所使用工具的實(shí)現(xiàn)吠谢?

探索知識的任何階段都是存有疑惑的土童,就像中學(xué)和大學(xué)都有數(shù)學(xué),但學(xué)習(xí)深入的程度不同工坊,分別有不同方面的疑惑献汗。我們只是基于一些公知的認(rèn)識,使其作為本階段學(xué)習(xí)的起點(diǎn)王污,并以此展開上層的研究罢吃。

少部分人會有一個無止盡思考的奇怪思維現(xiàn)象。舉例來說昭齐,我們都知道地球圍繞太陽做周期性公轉(zhuǎn)尿招,又知道電子圍繞原子核做周期性公轉(zhuǎn)運(yùn)動,這和地球繞太陽公轉(zhuǎn)的行為如出一轍阱驾,不禁會讓人想就谜,太陽是不是相當(dāng)于原子核,地球相當(dāng)于一個電子里覆,我們都生活在一個電子上丧荐。而我們的是身體里有那么多原子和電子,我們的身體是否又定義了另一個新的宇宙喧枷。無盡的遐想虹统,無盡的疑惑,雖然有些荒誕隧甚,但并非完全不合理车荔。但是如果無休止地問下去,雖然會對底層的科學(xué)更加清晰戚扳,但是對上層的知識結(jié)構(gòu)的構(gòu)建非常不利夸赫,從而我們需要一個公設(shè),例如認(rèn)為原子是不可再分的咖城,沒有更小的對象了茬腿,一切理論研究以此為基礎(chǔ)展開。例如乘法是基于加法的宜雀,在計(jì)算3*4的結(jié)果時(shí)切平,必須不去質(zhì)疑為什么1+1=2這件事,并認(rèn)為它是真理辐董。在學(xué)習(xí)操作系統(tǒng)時(shí)悴品,不去思考硬件內(nèi)部究竟是如何工作的,只假設(shè)硬件是一個給定輸入有給定輸出的系統(tǒng)。

這里的Java虛擬機(jī)苔严,作為一個運(yùn)行字節(jié)碼的平臺定枷,顯然不能當(dāng)做一個公設(shè)來看待。虛擬機(jī)包含三個概念届氢,語言規(guī)范欠窒,具體實(shí)現(xiàn)和運(yùn)行實(shí)例。語言規(guī)范描述了虛擬機(jī)需要實(shí)現(xiàn)什么退子,而并沒有規(guī)定需要如何去實(shí)現(xiàn)岖妄。復(fù)雜的虛擬機(jī)(Hotspot/JRockit/J9)和簡單的虛擬機(jī)(Kaffe/Jamvm/cacaovm)在實(shí)現(xiàn)方面有很大的差異。對一個有不確定實(shí)現(xiàn)的工具寂祥,沖一杯咖啡荐虐,打開音樂,從源代碼的角度分析工具的實(shí)現(xiàn)丸凭,對上層知識的構(gòu)建非常有利(事實(shí)也證明福扬,Java使用者對虛擬機(jī)的了解程度,遠(yuǎn)遠(yuǎn)不如c/c++使用者對機(jī)器平臺的了解程度深)惜犀。

以Hotspot為例忧换,在虛擬機(jī)的內(nèi)部,通過instanceOopDesc來表示一個對象(OOP-Klass二分模型在另一個篇中寫)向拆,每個對象包含Mark Word和元數(shù)據(jù)指針作為對象頭亚茬,接下來依次是實(shí)例數(shù)據(jù)和padding:

對象在內(nèi)存中分布

Mark Word: 定義在oopDesc中的_mark成員,儲存對象運(yùn)行時(shí)的記錄信息浓恳,如HashCode/GC分代年齡狀態(tài)鎖標(biāo)志/線程持有的鎖/偏向線程ID/偏向時(shí)間戳等刹缝。_mark成員的數(shù)據(jù)類型為markOop,占用內(nèi)存大小與虛擬機(jī)word長度一致颈将。在32位虛擬機(jī)上為32位梢夯,在64位虛擬機(jī)上為64位(可以壓縮)。

元數(shù)據(jù)指針: 定義在oopDesc中的_metadata成員晴圾,指向描述類型的Klass對象指針颂砸。根據(jù)是否壓縮定義為一個union。虛擬機(jī)在運(yùn)行時(shí)頻繁使用這個指針定位到位于方法區(qū)的信息死姚。

對象頭中的元信息

虛擬機(jī)運(yùn)行時(shí)人乓,每創(chuàng)建一個對象,在虛擬機(jī)內(nèi)部就要創(chuàng)建相應(yīng)有對象頭的對象都毒,因此對象頭的布局對對象內(nèi)存空間利用率(Instance Data/Header+instanceData+padding)十分重要色罚。但是,在對象生命周期內(nèi)账劲,虛擬機(jī)要記錄很多信息戳护,如hashCode/GC分代年齡/鎖記錄指針/線程ID 等金抡,因此header必須要仔細(xì)設(shè)計(jì)。

設(shè)計(jì)1: 虛擬機(jī)配置選項(xiàng)-XX:UserCompressedOops腌且。其作用是在64位機(jī)器上梗肝,對_metadata成員使用32位指針存儲。在64位系統(tǒng)上铺董,指針類型為64位巫击,這樣一來,從32位系統(tǒng)遷移到64位系統(tǒng)時(shí)柄粹,內(nèi)存利用率就會有所下降喘鸟。union聯(lián)合體中wideKlassOop是指向klassOopDesc指針匆绣,而narrowOop是32位無符號整形驻右。

設(shè)計(jì)2: _mark成員模仿網(wǎng)絡(luò)協(xié)議報(bào)文頭部,把mark word劃分為多個比特區(qū)間崎淳,并在不同對象狀態(tài)下賦予每個bit不同含義堪夭。

Hotspot有三種對象成員排列順序: [oops,longs/doubles/ints/shorts/chars/bytes],[longs/doubles, ints, shorts/chars, bytes, oops] 和 [Fields allocation: oops fields in super and sub classes are together]拣凹。默認(rèn)為第二種順序森爽。源碼如下:

根據(jù)allocation_style值的不同應(yīng)用不同的內(nèi)存排列模型

排列規(guī)則如下,每個對象內(nèi)存地址要是8的倍數(shù)嚣镜,每個成員的內(nèi)存地址要是自己大小的倍數(shù)爬迟,例如整形要是4字節(jié)的倍數(shù),long要是8字節(jié)的倍數(shù)菊匿。

假設(shè)付呕,在Java代碼中,定義如下類:

定義一個直接繼承Object的類

如果虛擬機(jī)不改變成員變量排列順序跌捆,32位機(jī)器徽职,在內(nèi)存中順序如下:

按Java代碼中定義順序排列

這樣有14字節(jié)因?yàn)閜adding被浪費(fèi)了。如果重新調(diào)整排序規(guī)則:

虛擬機(jī)重排序過后的排列

這樣只有6字節(jié)因?yàn)閜adding被浪費(fèi)佩厚。在每個成員都要內(nèi)存對齊的情況下姆钉,先分配大內(nèi)存的成員會節(jié)約內(nèi)存。

按照這個規(guī)則來計(jì)算Object對象的直接子類Boolean抄瓦,header+value+padding=8+1+7=16潮瓶,竟然需要16字節(jié)。

如果類不是直接繼承Object對象钙姊,即父類中如果有成員變量的話筋讨。舉例如下:

定義有成員函數(shù)的父類

一個B的實(shí)例在內(nèi)存中看起來長這樣:

應(yīng)用默認(rèn)規(guī)則,先排列父類成員再排列子類成員

其他情況的排序不再贅述摸恍,可根據(jù)代碼自行排列悉罕。

了解Boolean類內(nèi)存利用率很低以后赤屋,再說一下HashMap。對于應(yīng)用層的程序來說壁袄,這簡直是神器类早,只要創(chuàng)建了之后就可以不斷的丟東西進(jìn)去,添加刪除都是O(1)操作嗜逻,又快又好涩僻。不過引用第一位圖靈獎獲得者Alan Perlis的名言:“Lisp programmers know the value of everything but the cost of nothing.”,目的是想提醒我們做事情不要忘記背后的代價(jià)栈顷。對于HashMap來說逆日,代價(jià)主要是內(nèi)存的開銷,試想一下萄凤,Java沒有HashMap<boolean,boolean>只有HashMap<Boolean,Boolean>室抽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市靡努,隨后出現(xiàn)的幾起案子坪圾,更是在濱河造成了極大的恐慌,老刑警劉巖惑朦,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兽泄,死亡現(xiàn)場離奇詭異,居然都是意外死亡漾月,警方通過查閱死者的電腦和手機(jī)病梢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梁肿,“玉大人蜓陌,你說我怎么就攤上這事≌祸ǎ” “怎么了护奈?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長哥纫。 經(jīng)常有香客問我霉旗,道長,這世上最難降的妖魔是什么蛀骇? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任厌秒,我火速辦了婚禮,結(jié)果婚禮上擅憔,老公的妹妹穿的比我還像新娘鸵闪。我一直安慰自己,他們只是感情好暑诸,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布蚌讼。 她就那樣靜靜地躺著辟灰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪篡石。 梳的紋絲不亂的頭發(fā)上芥喇,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天,我揣著相機(jī)與錄音凰萨,去河邊找鬼继控。 笑死,一個胖子當(dāng)著我的面吹牛胖眷,可吹牛的內(nèi)容都是我干的武通。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼珊搀,長吁一口氣:“原來是場噩夢啊……” “哼冶忱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起食棕,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤朗和,失蹤者是張志新(化名)和其女友劉穎错沽,沒想到半個月后簿晓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡千埃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年憔儿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片放可。...
    茶點(diǎn)故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡谒臼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耀里,到底是詐尸還是另有隱情蜈缤,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布冯挎,位于F島的核電站底哥,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏房官。R本人自食惡果不足惜趾徽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望翰守。 院中可真熱鬧孵奶,春花似錦、人聲如沸蜡峰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至载绿,卻和暖如春僻肖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卢鹦。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工臀脏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人冀自。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓揉稚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親熬粗。 傳聞我的和親對象是個殘疾皇子搀玖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評論 2 361

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