一個Java對象到底佔用多大內存?

最近在調研MAT和VisualVM源碼實現,遇到一個可疑問題,兩者計算出來的對象大小不一致,才有了這樣疑惑。

一個Java對象到底佔用多大內存?

一个Java对象到底占用多大内存?

為了復現這個問題,準備了4個最簡單類:

  1. <code>class AAAAA {}/<code>


  2. <code>class BBBBB {/<code>

  3. <code>int a = 1;/<code>

  4. <code>}/<code>


  5. <code>class CCCCC {/<code>

  6. <code>long a = 1L;/<code>

  7. <code>}/<code>


  8. <code>class DDDDD {/<code>

  9. <code>Strings ="hello";/<code>

  10. <code>}/<code>

當然了,再來個主函數:

  1. <code>final List<aaaaa> aaa = newArrayList<>(100000);/<aaaaa>/<code>

  2. <code>final List<bbbbb> bbb = newArrayList<>(100000);/<bbbbb>/<code>

  3. <code>final List<ccccc> ccc = newArrayList<>(100000);/<ccccc>/<code>

  4. <code>final List<ddddd> ddd = newArrayList

    <>(100000);/<ddddd>/<code>

  5. <code>for (int i = 0; i <100000; i++) {/<code>

  6. <code>aaa.add(new AAAAA);/<code>

  7. <code>bbb.add(new BBBBB);/<code>

  8. <code>ccc.add(new CCCCC);/<code>

  9. <code>ddd.add(new DDDDD);/<code>

  10. <code>}/<code>

本地的執行環境是64位的JDK8,且使用默認的啟動參數,運行之後通過 <code>jmap-dump/<code>命令生成dump文件,分別用MAT和VisualVM打開。

MAT

一个Java对象到底占用多大内存?

通過MAT打開,可以發現ABD對象大小都是16字節,而C對象大小為24字節

VisualVM

一个Java对象到底占用多大内存?

通過Vis打開,可以發現其顯示的大小和MAT有蠻大的差別。

一个Java对象到底占用多大内存?

好奇怪,哪個是對的?

要回答這個問題,首先得清楚的知道JVM中對象的內存佈局。

在Hotspot中,一個對象包含3個部分:對象頭、實例數據和對齊填充

對象頭

這裡不講對象頭是個什麼東西,感興趣的同學可以看我的其它文章。對象頭的大小一般和系統的位數有關,也和啟動參數 <code>UseCompressedOops/<code>有關:

  • 32位系統,佔用 8 字節

  • 64位系統,開啟 <code>UseCompressedOops/<code>時,佔用 12 字節,否則是16字節

實例數據

原生類型的內存佔用情況如下:

  • boolean 1個字節

  • byte 1個字節

  • short 2個字節

  • char 2個字節

  • int 4個字節

  • float 4個字節

  • long 8個字節

  • double 8個字節

引用類型的內存佔用和系統位數以及啟動參數 <code>UseCompressedOops/<code>有關

  • 32位系統佔4字節

  • 64位系統,開啟 <code>UseCompressedOops/<code>時,佔用4字節,否則是8字節

對齊填充

在Hotspot中,為了更加容易的管理內存,一般會使用8字節進行對齊

意思是每次分配的內存大小一定是8的倍數,如果對象頭+實例數據的值不是8的倍數,那麼會重新計算一個較大值,進行分配。

結果

有了對象各部分的內存佔用大小,可以很輕鬆的計算出ABCD各對象在64位系統,且開啟 <code>UseCompressedOops/<code>參數時的大小。

  • A對象只包含一個對象頭,大小佔12字節,不是8的倍數,需要4字節進行填充,一共佔16字節

  • B對象包含一個對象頭和int類型,12+4=16,正好是8的倍數,不需要填充。

  • C對象包含一個對象頭和long類型,12+8=20,不是8的倍數,需要4個字節進行填充,佔24字節

  • D對象包含一個對象頭和引用類型,12+4=16,正好是8的倍數,不需要填充。

可以得出,VisualVM的顯示結果有點問題,主要因為以下兩點:

  • 首先,沒有考慮是否開啟 <code>UseCompressedOops;/<code>

  • 其次,沒有考慮內存對齊填充的情況;

感興趣的同學,可以動手實踐一下,這樣可以加深對象內存佈局的理解。

經過這段時間對MAT和VisualVM的源碼研究,發現MAT的功能不是強大一點點,建議大家以後儘量使用MAT。


分享到:


相關文章: