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实例的情况




