一個對象實例佔用了多少字節,消耗了多少內存?這樣的問題在c或c++裏使用sizeof()方法就能夠獲得明確答案,在java裏好像沒有這樣的方法(java同樣能夠實現),不過經過jmap工具卻是能夠查看出一個對象的佔用內存的大小,這裏介紹一種經過分析java代碼計算內存使用狀況的方法。java
注意,一下討論的狀況都是基於32位機,不適用用64位機,JVM是sun的HotSpot,不一樣的虛擬機實現可能會不一樣c++
規則一:每一個對象被按照8bytes粒度對齊(數組除外)數組
在jvm中每一個對象(數組除外)都有一個頭,這個頭有兩個字,第一個字存儲的時對象的一些標誌位信息,例如:鎖標誌位、經歷了幾回gc等信息,第二個字是一個引用,指向了這個類的類信息。這裏jvm給這兩個字留了8個字節的空間(這個爲啥用8個字節空間不是很清楚,一個字即兩個字節,我一直認爲4個字節就夠了)jvm
按規則一:new Object();這個Object實例就佔用了8個字節工具
規則二:爲類屬性分配存儲空間時不是按照類中定義的屬性順序,而是按以下的順序:對象
一、double\long;----8bytes繼承
二、int\float;----4bytes內存
三、char\short;----2bytes虛擬機
四、boolean\byte;----1bytesclass
五、reference;----4bytes
例如:
Java代碼
public class A {
byte a;
char b;
int c;
long d;
Object e;
}
屬性 須要字節數 累積字節數
header 8bytes 8
long:d 8bytes 16
int:c 4bytes 20
char:b 2bytes 22
byte:a 1bytes 23
Object:e 4bytes 27
padding 5bytes 32
最後一行padding 5bytes的目的是,規則一中描述每一個對象按照8個字節的粒度對齊,這樣下一個分配的對象的開始位置必須在8的倍數上,而離27最近的8的倍數是32,所以加了5bytes。A佔用32bytes
能夠用jmap看一下這個計算是否準確
規則三:對於繼承時,要按照規則二先計算父類的類屬性佔用狀況,再按照規則二計算子類的類屬性佔用狀況,不能將父類和子類的屬性混合在一塊兒按規則二分配。
例如:
Java代碼
class B{
long a;
int b;
int c;
}
class BB extends B{
long d;
}
屬性 佔用字節數 累計字節數
header 8 8
a 8 16
b 4 20
c 4 24
d 8 32
這裏累計字節正好是8的倍數,知足規則一,所以不用padding字節。BB對象內存佔用了32bytes
規則四:父類的最後一個屬性和子類第一個屬性必須按4個字節的倍數對齊
例如:
Java代碼
class B{
long a;
int b;
char c;
}
class BB extends B{
long d;
}
屬性 佔用的字節 累計字節
head 8 8
a 8 16
c 2 18
padding 2 20
d 8 28
padding 4 32
第一次padding2是由於屬性c分配內存後,不知足父類最後一個屬性和子類第一個屬性按4字節粒度對齊(18除4除不開),所以須要添加兩個字節使其能夠按4字節粒度對齊。
第二次paadding4是依據規則一
規則五:當子類的第一個屬性是double或long,可是父類不能按8字節粒度對齊時,子類內存分配時的順序將不按規則二進行,而是按:先int\float、char\short、boolean\byte、reference、long\double
例如:
Java代碼
class A{
byte a;
}
class B extends A{
long b;
short c;
byte d;
}
屬性 佔用字節數 累計佔用字節數
head 8 8
a 1 9
padding 3 12
c 2 14
d 1 15
padding 1 16
b 8 24
第一次 padding 3是根據規則四
第二次 padding 1是由於b屬性是8個字節,所以須要按8個字節粒度對齊。B佔用24bytes
對於數組,與普通對象不一樣的是在頭部,頭部多了4個字節用於存儲長度信息。所以數組的head是12bytes而不是8bytes