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

大家可以用這個代碼邊看邊驗證,注意的是,運行這個程序需要通過javaagent注入Instrumentation,具體可以看原博客。我今天主要是總結下手動計算Java對象佔用字節數的基本規則,做爲基本的技能必須get√,希望能幫到和我一樣的Java菜鳥


1.png
2.png
3.png

在介紹之前,簡單回顧下,Java對象的內存佈局:對象頭(Header),實例數據(Instance Data)和對齊填充(Padding),詳細的可以看我的讀書筆記。另外:不同的環境結果可能有差異,我所在的環境是HotSpot虛擬機,64位Windwos。
下面進入正文:
對象頭
對象頭在32位系統上佔用8bytes,64位系統上佔用16bytes。
實例數據
原生類型(primitive type)的內存佔用如下:
Primitive Type
Memory Required(bytes)
boolean
1
byte
1
short
2
char
2
int
4
float
4
long
8
double
8
reference類型在32位系統上每個佔用4bytes, 在64位系統上每個佔用8bytes。
對齊填充
HotSpot的對齊方式爲8字節對齊:
(對象頭 + 實例數據 + padding) % 8等於0且0 <= padding < 8
指針壓縮
對象佔用的內存大小收到VM參數UseCompressedOops的影響。
1)對對象頭的影響
開啓(-XX:+UseCompressedOops)對象頭大小爲12bytes(64位機器)。
static class A {        int a;    }
A對象佔用內存情況:
關閉指針壓縮: 16+4=20不是8的倍數,所以+padding/4=24
開啓指針壓縮: 12+4=16已經是8的倍數了,不需要再padding。
1)對reference類型的影響
64位機器上reference類型佔用8個字節,開啓指針壓縮後佔用4個字節。
static class B2 {        int b2a;        Integer b2b;}
B2對象佔用內存情況:
關閉指針壓縮: 16+4+8=28不是8的倍數,所以+padding/4=32
開啓指針壓縮: 12+4+4=20不是8的倍數,所以+padding/4=24
數組對象
64位機器上,數組對象的對象頭佔用24個字節,啓用壓縮之後佔用16個字節。之所以比普通對象佔用內存多是因爲需要額外的空間存儲數組的長度。
先考慮下new Integer[0]佔用的內存大小,長度爲0,即是對象頭的大小:
未開啓壓縮:24bytes
開啓壓縮後:16bytes
接着計算new Integer[1],new Integer[2],new Integer[3]和new Integer[4]就很容易了:
未開啓壓縮:
開啓壓縮:
拿new Integer[3]來具體解釋下:
未開啓壓縮:24(對象頭)+8*3=48,不需要padding;
開啓壓縮:16(對象頭)+3*4=28,+padding/4=32,其他依次類推。
自定義類的數組也是一樣的,比如:
static class B3 {        int a;        Integer b;    }
new B3[3]佔用的內存大小:
未開啓壓縮:48
開啓壓縮後:32
複合對象
計算複合對象佔用內存的大小其實就是運用上面幾條規則,只是麻煩點。
1)對象本身的大小
直接計算當前對象佔用空間大小,包括當前類及超類的基本類型實例字段大小、引用類型實例字段引用大小、實例基本類型數組總佔用空間、實例引用類型數組引用本身佔用空間大小; 但是不包括超類繼承下來的和當前類聲明的實例引用字段的對象本身的大小、實例引用數組引用的對象本身的大小。
static class B {        int a;     
   int b;    }
static class C {   
     int ba;      
  B[] as = new B[3];   
     C() {         
   for (int i = 0; i < as.length; i++) {    
            as = new B();        
    }        }    }
未開啓壓縮:16(對象頭)+4(ba)+8(as引用的大小)+padding/4=32
開啓壓縮:12+4+4+padding/4=24
2)當前對象佔用的空間總大小
遞歸計算當前對象佔用空間總大小,包括當前類和超類的實例字段大小以及實例字段引用對象大小。
遞歸計算複合對象佔用的內存的時候需要注意的是:對齊填充是以每個對象爲單位進行的,看下面這個圖就很容易明白。
現在我們來手動計算下C對象佔用的全部內存是多少,主要是三部分構成:C對象本身的大小+數組對象的大小+B對象的大小。
未開啓壓縮:
(16 + 4 + 8+4(padding)) + (24+ 8*3) +(16+8)*3 = 152bytes
開啓壓縮:
(12 + 4 + 4 +4(padding)) + (16 + 4*3 +4(數組對象padding)) + (12+8+4(B對象padding))*3= 128bytes
大家有興趣的可以試試。
實際工作中真正需要手動計算對象大小的場景應該很少,但是個人覺得做爲基礎知識每個Java開發人員都應該瞭解,另外:對自己寫的代碼大概佔用多少內存,內存中是怎麼佈局的應該有一個直覺性的認識。
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章