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開發中, 想要進行內存分析, 總會看見Shallow Size
和Retained Size
, 這邊文章主要解釋
- 它們分別表示什麼含義
- 它們是如何計算出來的
Java garbage collection (GC)
我們先了解GC
的一些基本知識
- 程序中存在一些實例, 稱作
GC root
, 它們不會被GC
回收, 常見的例如靜態變量, 線程等 - 被
GC root
直接或間接引用的實例會被標記爲in use
, 它們也不會被GC
回收
Shallow Size
Shallow Size
是指實例自身佔用的內存, 可以理解爲保存該'數據結構'需要多少內存, 注意不包括它引用的其他實例
計算公式:
Shallow Size = [類定義] + 父類fields所佔空間 + 自身fields所佔空間 + [alignment]
-
類定義
是指, 聲明一個類本身所需的空間, 固定爲8byte, 也就是說, 一個不包含任何fields的類的'空類', 也需要佔8byte; 另外類定義空間不會重複計算, 就是說, 即使類繼承其他類, 也只算8byte -
父類fields所佔空間
, 對於繼承了其他類的類來說, 父類聲明的fields顯然需要佔用一定的空間 -
自身fields所佔空間
, 所有fields所佔空間之和; fields分基本類型和引用, 基本類型所佔空間和系統有關, 例如在32位系統中int=4byte, 64位系統中int=8byte; 引用固定佔4byte, 例如String name;
這個變量聲明佔4byte. -
alignment
是指位數對齊, 會讓總空間爲8的倍數, 例如某個A類, 以上3項計算出來爲15byte, 那麼爲了對齊, 讓它是8的倍數, 會取最接近的值, 所以它的Shallow Size是16byte;
注意,
alignment
行爲和JVM有關, 對於Android來說, 實測4.4系統會有對齊行爲, 但是5.1系統不會
Shallow Size例子
class X {
int a;
byte b;
Integer c = new Integer();
}
假設當前是在32位系統, 對於類X來說, 一個X實例的Shallow Size爲:
- 類定義的8byte
- 沒有繼承其他類, 所以沒有父類fields
- a變量爲int型, 4byte; b變量爲byte型, 1byte; c變量是引用類型, 和它是否指向具體實例無關, 固定佔4byte
如果不算alignment
,
X的Shallow Size = 8 + 0 + 4 + 1 + 4 = 17byte
如果算上alignment
, 那麼要補齊爲8的倍數, 也就是24byte.
class Y extends X {
List d;
Date e;
}
一個Y實例的Shallow Size爲:
- 類定義的8byte
- 繼承了X類, X類的所有fields爲X類的Shallow Size減去類定義空間8byte, 也就是17byte-8byte=9byte
- d, e都是引用類型, 各佔4byte
如果不算alignment
,
Y的Shallow Size = 8 + 9 + 4 + 4 = 25byte
如果算上alignment
, 那麼要補齊爲8的倍數, 也就是32byte.
Retained Size
實例A的
Retained Size
是指, 當實例A被回收時, 可以同時被回收的實例的Shallow Size之和
所以進行內存分析時, 我們應該重點關注Retained Size較大的實例; 或者可以通過Retained Size判斷出某A實例內部使用的實例是否被其他實例引用.
例如在Android中, 如果某個Bitmap
實例的Retained Size很小, 證明它內部的byte數組被複用了, 有另一個Bitmap
實例指向了同一個byte數組.
Retained Size例子
圖中A, B, C, D四個實例, 爲了方便計算, 我們假設所有實例的Shallow Size都是1kb
D實例
D實例沒有引用其他實例, 所以移除D實例只會釋放它自己的空間, 因此
D實例的Retained Size=Shallow Size=1kb
C實例
當我們移除C實例, C實例引用了D實例, 同時D實例沒有被其他實例引用, 所以D實例也會被GC, 所以
C實例的Retained Size = C實例的Shallow Size + D實例的Shallow Size = 2kb
B實例
當我們移除B實例, 雖然B實例引用了C實例, 但是A實例也引用了C實例, 所以移除B實例不會讓C實例被GC, 所以
B實例的Retained Size=Shallow Size=1kb
A實例
當我們移除A實例, 顯然A, B, C, D實例都會被GC, 所以
A實例的Retained Size=4kb
總結
計算Retained Size的關鍵在於領會移除實例時, 可以同時被回收的實例
, 重點觀察B實例的情況