Shallow Size和Retained Size详解
前言
在内存快照分析中,想要进行内存分析,总会看见 Shallow Size
和 Retained Size
, 这篇文章主要解释
它们分别表示什么含义
它们是如何计算出来的
Java garbage collection (GC)
我们先了解 GC
的一些基本知识
程序中存在一些实例,称作
GC root
, 它们不会被GC
回收,常见的例如静态变量,线程等被
GC root
直接或间接引用的实例会被标记为in use
, 它们也不会被GC
回收
Shallow Size
Shallow Size
是指实例自身占用的内存,可以理解为保存该’数据结构’需要多少内存,注意不包括它引用的其他实例
计算公式:
1 | 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 例子
1 | class X { |
假设当前是在 32 位系统 , 对于类 X 来说,一个 X 实例的 Shallow Size 为:
- 类定义的 8byte
- 没有继承其他类,所以没有父类 fields
- a 变量为 int 型,4byte; b 变量为 byte 型,1byte; c 变量是引用类型,和它是否指向具体实例无关,固定占 4byte
如果不算alignment
,
1 | X的Shallow Size = 8 + 0 + 4 + 1 + 4 = 17byte |
如果算上alignment
, 那么要补齐为 8 的倍数,也就是 24byte.
1 | class Y extends X { |
一个 Y 实例的 Shallow Size 为:
- 类定义的 8byte
- 继承了 X 类,X 类的所有 fields 为 X 类的 Shallow Size 减去类定义空间 8byte, 也就是 17byte-8byte=9byte
- d, e 都是引用类型,各占 4byte
如果不算alignment
,
1 | 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 例子
RetainedSize 例子.png
图中 A, B, C, D 四个实例,为了方便计算,我们假设所有实例的 Shallow Size 都是 1kb
D 实例
D 实例没有引用其他实例,所以移除 D 实例只会释放它自己的空间,因此
1 | D实例的Retained Size=Shallow Size=1kb |
C 实例
当我们移除 C 实例,C 实例引用了 D 实例,同时 D 实例没有被其他实例引用 , 所以 D 实例也会被 GC, 所以
1 | C实例的Retained Size = C实例的Shallow Size + D实例的Shallow Size = 2kb |
B 实例
当我们移除 B 实例,虽然 B 实例引用了 C 实例,但是 A 实例也引用了 C 实例,所以移除 B 实例不会让 C 实例被 GC, 所以
1 | B实例的Retained Size=Shallow Size=1kb |
A 实例
当我们移除 A 实例,显然 A, B, C, D 实例都会被 GC, 所以
1 | A实例的Retained Size=4kb |
总结
计算 Retained Size 的关键在于领会移除实例时, 可以同时被回收的实例
, 重点观察 B 实例的情况