Shallow Size和Retained Size詳解
參考文章
How much memory do I need (part 1) – What is retained heap?
How much memory do I need (part 2) – What is shallow heap?
在Android開(kāi)發(fā)中, 想要進(jìn)行內(nèi)存分析, 總會(huì)看見(jiàn)Shallow Size和Retained Size, 這邊文章主要解釋
- 它們分別表示什么含義
- 它們是如何計(jì)算出來(lái)的
Java garbage collection (GC)
我們先了解GC的一些基本知識(shí)
- 程序中存在一些實(shí)例, 稱(chēng)作
GC root, 它們不會(huì)被GC回收, 常見(jiàn)的例如靜態(tài)變量, 線程等 - 被
GC root直接或間接引用的實(shí)例會(huì)被標(biāo)記為in use, 它們也不會(huì)被GC回收
Shallow Size
Shallow Size是指實(shí)例自身占用的內(nèi)存, 可以理解為保存該'數(shù)據(jù)結(jié)構(gòu)'需要多少內(nèi)存, 注意不包括它引用的其他實(shí)例
計(jì)算公式:
Shallow Size = [類(lèi)定義] + 父類(lèi)fields所占空間 + 自身fields所占空間 + [alignment]
-
類(lèi)定義是指, 聲明一個(gè)類(lèi)本身所需的空間, 固定為8byte, 也就是說(shuō), 一個(gè)不包含任何fields的類(lèi)的'空類(lèi)', 也需要占8byte; 另外類(lèi)定義空間不會(huì)重復(fù)計(jì)算, 就是說(shuō), 即使類(lèi)繼承其他類(lèi), 也只算8byte -
父類(lèi)fields所占空間, 對(duì)于繼承了其他類(lèi)的類(lèi)來(lái)說(shuō), 父類(lèi)聲明的fields顯然需要占用一定的空間 -
自身fields所占空間, 所有fields所占空間之和; fields分基本類(lèi)型和引用, 基本類(lèi)型所占空間和系統(tǒng)有關(guān), 例如在32位系統(tǒng)中int=4byte, 64位系統(tǒng)中int=8byte; 引用固定占4byte, 例如String name;這個(gè)變量聲明占4byte. -
alignment是指位數(shù)對(duì)齊, 會(huì)讓總空間為8的倍數(shù), 例如某個(gè)A類(lèi), 以上3項(xiàng)計(jì)算出來(lái)為15byte, 那么為了對(duì)齊, 讓它是8的倍數(shù), 會(huì)取最接近的值, 所以它的Shallow Size是16byte;
注意,
alignment行為和JVM有關(guān), 對(duì)于Android來(lái)說(shuō), 實(shí)測(cè)4.4系統(tǒng)會(huì)有對(duì)齊行為, 但是5.1系統(tǒng)不會(huì)
Shallow Size例子
class X {
int a;
byte b;
Integer c = new Integer();
}
假設(shè)當(dāng)前是在32位系統(tǒng), 對(duì)于類(lèi)X來(lái)說(shuō), 一個(gè)X實(shí)例的Shallow Size為:
- 類(lèi)定義的8byte
- 沒(méi)有繼承其他類(lèi), 所以沒(méi)有父類(lèi)fields
- a變量為int型, 4byte; b變量為byte型, 1byte; c變量是引用類(lèi)型, 和它是否指向具體實(shí)例無(wú)關(guān), 固定占4byte
如果不算alignment,
X的Shallow Size = 8 + 0 + 4 + 1 + 4 = 17byte
如果算上alignment, 那么要補(bǔ)齊為8的倍數(shù), 也就是24byte.
class Y extends X {
List d;
Date e;
}
一個(gè)Y實(shí)例的Shallow Size為:
- 類(lèi)定義的8byte
- 繼承了X類(lèi), X類(lèi)的所有fields為X類(lèi)的Shallow Size減去類(lèi)定義空間8byte, 也就是17byte-8byte=9byte
- d, e都是引用類(lèi)型, 各占4byte
如果不算alignment,
Y的Shallow Size = 8 + 9 + 4 + 4 = 25byte
如果算上alignment, 那么要補(bǔ)齊為8的倍數(shù), 也就是32byte.
Retained Size
實(shí)例A的
Retained Size是指, 當(dāng)實(shí)例A被回收時(shí), 可以同時(shí)被回收的實(shí)例的Shallow Size之和
所以進(jìn)行內(nèi)存分析時(shí), 我們應(yīng)該重點(diǎn)關(guān)注Retained Size較大的實(shí)例; 或者可以通過(guò)Retained Size判斷出某A實(shí)例內(nèi)部使用的實(shí)例是否被其他實(shí)例引用.
例如在Android中, 如果某個(gè)Bitmap實(shí)例的Retained Size很小, 證明它內(nèi)部的byte數(shù)組被復(fù)用了, 有另一個(gè)Bitmap實(shí)例指向了同一個(gè)byte數(shù)組.
Retained Size例子

圖中A, B, C, D四個(gè)實(shí)例, 為了方便計(jì)算, 我們假設(shè)所有實(shí)例的Shallow Size都是1kb
D實(shí)例
D實(shí)例沒(méi)有引用其他實(shí)例, 所以移除D實(shí)例只會(huì)釋放它自己的空間, 因此
D實(shí)例的Retained Size=Shallow Size=1kb
C實(shí)例
當(dāng)我們移除C實(shí)例, C實(shí)例引用了D實(shí)例, 同時(shí)D實(shí)例沒(méi)有被其他實(shí)例引用, 所以D實(shí)例也會(huì)被GC, 所以
C實(shí)例的Retained Size = C實(shí)例的Shallow Size + D實(shí)例的Shallow Size = 2kb
B實(shí)例
當(dāng)我們移除B實(shí)例, 雖然B實(shí)例引用了C實(shí)例, 但是A實(shí)例也引用了C實(shí)例, 所以移除B實(shí)例不會(huì)讓C實(shí)例被GC, 所以
B實(shí)例的Retained Size=Shallow Size=1kb
A實(shí)例
當(dāng)我們移除A實(shí)例, 顯然A, B, C, D實(shí)例都會(huì)被GC, 所以
A實(shí)例的Retained Size=4kb
總結(jié)
計(jì)算Retained Size的關(guān)鍵在于領(lǐng)會(huì)移除實(shí)例時(shí), 可以同時(shí)被回收的實(shí)例, 重點(diǎn)觀察B實(shí)例的情況