最近在調(diào)研MAT和VisualVM源碼實(shí)現(xiàn)呀打,遇到一個(gè)可疑問(wèn)題榜贴,兩者計(jì)算出來(lái)的對(duì)象大小不一致豌研,該信哪個(gè)?
為了復(fù)現(xiàn)這個(gè)問(wèn)題,準(zhǔn)備了4個(gè)簡(jiǎn)單類(lèi):
class AAAAA {}
class BBBBB {
int a = 1;
}
class CCCCC {
long a = 1L;
}
class DDDDD {
String s = "hello";
}
再來(lái)個(gè)主函數(shù):
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());
}
本地環(huán)境是64位的JDK8鹃共,默認(rèn)的啟動(dòng)參數(shù)鬼佣,運(yùn)行之后通過(guò)jmap -dump
命令生成dump文件,分別用MAT和VisualVM打開(kāi)霜浴。
MAT
通過(guò)MAT打開(kāi)晶衷,可以發(fā)現(xiàn)ABD對(duì)象大小都是16字節(jié),反而C對(duì)象大小為24字節(jié)
VisualVM
通過(guò)VisualVM打開(kāi)阴孟,可以發(fā)現(xiàn)顯示的大小和MAT的有蠻大的差別晌纫。
哪個(gè)才是正確的?
要回答這個(gè)問(wèn)題温眉,首先得清楚的知道JVM中一個(gè)對(duì)象的內(nèi)存布局缸匪。
JVM中一個(gè)對(duì)象包含3個(gè)部分:對(duì)象頭、實(shí)例數(shù)據(jù)和對(duì)齊填充类溢。
對(duì)象頭
這里不講對(duì)象頭是個(gè)什么東西凌蔬,感興趣的同學(xué)可以看我的其它文章。
對(duì)象頭的大小一般和系統(tǒng)的位數(shù)有關(guān)闯冷,也和啟動(dòng)參數(shù)UseCompressedOops
有關(guān):
- 32位系統(tǒng)砂心,占用 8 字節(jié)
- 64位系統(tǒng),開(kāi)啟
UseCompressedOops
時(shí)蛇耀,占用 12 字節(jié)辩诞,否則是16字節(jié)
實(shí)例數(shù)據(jù)
原生類(lèi)型的內(nèi)存占用情況如下:
- boolean 1
- byte 1
- short 2
- char 2
- int 4
- float 4
- long 8
- double 8
引用類(lèi)型的內(nèi)存占用和系統(tǒng)位數(shù)以及啟動(dòng)參數(shù)UseCompressedOops
有關(guān)
- 32位系統(tǒng)占4字節(jié)
- 64位系統(tǒng),開(kāi)啟
UseCompressedOops
時(shí)纺涤,占用4字節(jié)译暂,否則是8字節(jié)
對(duì)齊填充
在Hotspot中,為了更加容易的管理內(nèi)存撩炊,一般會(huì)使用8字節(jié)進(jìn)行對(duì)齊外永。
意思是每次分配的內(nèi)存大小一定是8的倍數(shù),如果對(duì)象頭+實(shí)例數(shù)據(jù)的值不是8的倍數(shù)拧咳,那么會(huì)重新計(jì)算一個(gè)較大值伯顶,進(jìn)行分配。
結(jié)果
有了對(duì)象各部分的內(nèi)存占用大小骆膝,可以很輕松的計(jì)算出ABCD各對(duì)象在64位系統(tǒng)祭衩,且開(kāi)啟UseCompressedOops
參數(shù)時(shí)的大小。
- A對(duì)象只包含一個(gè)對(duì)象頭阅签,大小占12字節(jié)掐暮,不是8的倍數(shù),需要加上4字節(jié)進(jìn)行填充政钟,一共占16字節(jié)
- B對(duì)象包含一個(gè)對(duì)象頭和int類(lèi)型劫乱,12+4=16织中,正好是8的倍數(shù),不需要填充衷戈。
- C對(duì)象包含一個(gè)對(duì)象頭和long類(lèi)型狭吼,12+8=20,不是8的倍數(shù)殖妇,使用4個(gè)字節(jié)進(jìn)行填充刁笙,占24字節(jié)
- D對(duì)象包含一個(gè)對(duì)象頭和引用類(lèi)型,12+4=16谦趣,正好是8的倍數(shù)疲吸,不需要填充。
所以前鹅,VisualVM的顯示結(jié)果有問(wèn)
- 首先摘悴,沒(méi)有考慮是否開(kāi)啟
UseCompressedOops
- 其次,沒(méi)有考慮對(duì)齊填充的情況
感興趣的同學(xué)舰绘,可以動(dòng)手實(shí)踐一下蹂喻,加深對(duì)對(duì)象內(nèi)存布局的印象。
經(jīng)過(guò)這段時(shí)間對(duì)MAT和VisualVM源碼的研究捂寿,發(fā)現(xiàn)MAT的功能不是強(qiáng)大一點(diǎn)點(diǎn)口四,建議大家以后都使用MAT對(duì)dump文件進(jìn)行分析。