Java基礎(chǔ) | 對象在內(nèi)存中的存儲布局以及如何計算對象的大小

總結(jié)圖

image

Java 內(nèi)存結(jié)構(gòu)

第一部分:對象頭

  1. markword:用于存儲對象自身的運(yùn)行時數(shù)據(jù)玄货,如哈希碼行嗤、GC分代年齡、鎖狀態(tài)標(biāo)志嘿架、線程持有的鎖等瓶珊。這部分?jǐn)?shù)據(jù)長度在32位機(jī)器和64位機(jī)器虛擬機(jī)中分別為4字節(jié)和8字節(jié)(64位的JVM為了節(jié)約內(nèi)存可以使用選項+UseCompressedOops開啟指針壓縮,開啟該選項后耸彪,占用字節(jié)數(shù)降為4字節(jié))伞芹;

    image

  2. 類型指針:即對象指向它的類元數(shù)據(jù)(保存在方法區(qū))的指針,虛擬機(jī)通過這個指針來確定這個對象屬于哪個類的實例蝉娜,指針占用4個字節(jié)(64位機(jī)器占8個字節(jié))唱较;

  3. 數(shù)組長度(只有數(shù)組對象才有):如果是 Java 數(shù)組,對象頭必須有一塊用于記錄數(shù)組長度的數(shù)據(jù)召川,用4個字節(jié)int來記錄數(shù)組長度南缓;

第二部分:實例數(shù)據(jù)

實例數(shù)據(jù)是對象真正存儲的有效信息,也是程序代碼中定義的各種類型的字段內(nèi)容荧呐。無論是從父類繼承下來還是在子類中定義
的數(shù)據(jù)汉形,都需要記錄下來。

原生類型的內(nèi)存占用情況如下:

  • boolean 1
  • byte 1
  • short 2
  • char 2
  • int 4
  • float 4
  • long 8
  • double 8

引用類型的內(nèi)存占用和系統(tǒng)位數(shù)以及啟動參數(shù)UseCompressedOops有關(guān)

  • 32位系統(tǒng)占4字節(jié)
  • 64位系統(tǒng)倍阐,開啟 UseCompressedOops時概疆,占用4字節(jié),否則是8字節(jié)

第三部分:對齊填充

對于hotspot迅疾的自動內(nèi)存管理系統(tǒng)要求對象的起始地址必須為8字節(jié)的整數(shù)倍峰搪,這就要求當(dāng)部位8字節(jié)的整數(shù)倍時岔冀,就需要填充數(shù)據(jù)對其填充。原因是訪問未對齊的內(nèi)存概耻,處理器需要做兩次內(nèi)存訪問使套,而對齊的內(nèi)存訪問僅需一次訪問罐呼。

如何計算或者獲取某個 Java 對象的大小?

32 位系統(tǒng)下,當(dāng)使用 new Object() 時童漩,JVM 將會分配 8(Mark Word+類型指針) 字節(jié)的空間弄贿,128 個 Object 對象將占用 1KB 的空間。

如果是 new Integer()矫膨,那么對象里還有一個 int 值差凹,其占用 4 字節(jié),這個對象也就是 8+4=12 字節(jié)侧馅,對齊后危尿,該對象就是 16 字節(jié)。

以上只是一些簡單的對象馁痴,那么對象的內(nèi)部屬性是怎么排布的谊娇?

Class A {
    int i;
    byte b;
    String str;
}

其中對象頭部占用 「Mark Word」4 + 「類型指針」4 = 8 字節(jié);byte 8 位長罗晕,占用 1 字節(jié)济欢;int 32 位長,占用 4 字節(jié)小渊;String 只有引用法褥,占用 4 字節(jié);

那么對象 A 一共占用了 8+1+4+4=17 字節(jié)酬屉,按照 8 字節(jié)對齊原則半等,對象大小也就是 24 字節(jié)。

這個計算看起來是沒有問題的呐萨,對象的大小也確實是 24 字節(jié)杀饵,但是對齊(padding)的位置并不對:

在 HotSpot VM 中,對象排布時谬擦,間隙是在 4 字節(jié)基礎(chǔ)上的(在 32 位和 64 位壓縮模式下)切距,上述例子中,int 后面的 byte惨远,空隙只剩下 3 字節(jié)蔚舀,接下來的 String 對象引用需要 4 字節(jié)來存放,因此 byte 和對象引用之間就會有 3 字節(jié)對齊锨络,對象引用排布后,最后會有 4 字節(jié)對齊狼牺,因此結(jié)果上依然是 7 字節(jié)對齊羡儿。此時對象的結(jié)構(gòu)示意圖,如下圖所示:

image

代碼驗證

public class Main {
    public static void main(String[] args) throws Exception {
        final List<AAAAA> aaa = new ArrayList<>(100000);
        final List<BBBBB> bbb = new ArrayList<>(100000);
        final List<CCCCC> ccc = new ArrayList<>(100000);
        final List<DDDDD> ddd = new ArrayList<>(100000);

        for (int i = 0; i < 100000; i++) {
            aaa.add(new AAAAA());
            bbb.add(new BBBBB());
            ccc.add(new CCCCC());
            ddd.add(new DDDDD());
        }

        System.in.read();
    }
}

class AAAAA {
}

class BBBBB {
    int a = 1;
}

class CCCCC {
    long a = 1L;
}

class DDDDD {
    String s = "hello";
}

本地的執(zhí)行環(huán)境是64位的JDK8是钥,且使用默認(rèn)的啟動參數(shù)掠归,運(yùn)行之后通過 jmap-dump命令生成dump文件缅叠,用MAT打開

image
  • A對象只包含一個對象頭,大小占12字節(jié)(markword 4 字節(jié)虏冻,類型指針 8 字節(jié))肤粱,不是8的倍數(shù),需要4字節(jié)進(jìn)行填充厨相,一共占16字節(jié)
  • B對象包含一個對象頭和int類型领曼,12+4=16,正好是8的倍數(shù)蛮穿,不需要填充庶骄,占16字節(jié)。
  • C對象包含一個對象頭和long類型践磅,12+8=20单刁,不是8的倍數(shù),需要4個字節(jié)進(jìn)行填充府适,占24字節(jié)
  • D對象包含一個對象頭和引用類型羔飞,12+4=16,正好是8的倍數(shù)檐春,不需要填充逻淌,占16字節(jié)。

因為 UseCompressedOops 在 JDK8 是是默認(rèn)開啟的喇聊,為了驗證不開啟時的內(nèi)存占用情況恍风,可以添加參數(shù) -XX:-UseCompressedOops 主動關(guān)閉指針壓縮,結(jié)果如下

image
image.gif

?

  • A對象只包含一個對象頭誓篱,大小占16字節(jié)(markword 8 字節(jié)朋贬,類型指針 8 字節(jié)),一共占16字節(jié)
  • B對象包含一個對象頭和int類型窜骄,16+4=20锦募,填充后占 24 字節(jié)。
  • C對象包含一個對象頭和long類型邻遏,16+8=24糠亩,占24字節(jié)
  • D對象包含一個對象頭和引用類型,16+8=24准验,占 24 字節(jié)赎线。

參考資料

https://www.cnblogs.com/magialmoon/p/3757767.html
https://mp.weixin.qq.com/s/BfWMp-3vPcg1eMgL4D249g

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市糊饱,隨后出現(xiàn)的幾起案子垂寥,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滞项,死亡現(xiàn)場離奇詭異狭归,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)文判,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門过椎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人戏仓,你說我怎么就攤上這事疚宇。” “怎么了柜去?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵灰嫉,是天一觀的道長。 經(jīng)常有香客問我嗓奢,道長讼撒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任股耽,我火速辦了婚禮根盒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘物蝙。我一直安慰自己炎滞,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布诬乞。 她就那樣靜靜地躺著册赛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪震嫉。 梳的紋絲不亂的頭發(fā)上森瘪,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機(jī)與錄音票堵,去河邊找鬼扼睬。 笑死,一個胖子當(dāng)著我的面吹牛悴势,可吹牛的內(nèi)容都是我干的窗宇。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼特纤,長吁一口氣:“原來是場噩夢啊……” “哼军俊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起捧存,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蝇完,失蹤者是張志新(化名)和其女友劉穎官硝,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體短蜕,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年傻咖,在試婚紗的時候發(fā)現(xiàn)自己被綠了朋魔。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡卿操,死狀恐怖警检,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情害淤,我是刑警寧澤扇雕,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站窥摄,受9級特大地震影響镶奉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜崭放,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一哨苛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧币砂,春花似錦建峭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至掌桩,卻和暖如春边锁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拘鞋。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工砚蓬, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盆色。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓灰蛙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親隔躲。 傳聞我的和親對象是個殘疾皇子摩梧,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355