Android优化篇|Shallow Size & Retained Size

5,871 阅读4分钟

概述

最近在分析一个内存占用问题,就通过 AS 的 Memory Profile (卡是真的卡)去查看了一下内存情况,看到了两个 Size,官方解释:

  • Shallow Size: Total amount of Java memory used by this object type (in bytes); Size of this instance in Java memory.
  • Retained Size: Total size of memory being retained due to all instances of this class (in bytes); Size of memory that this instance dominates (as per the dominator tree).

类似下图(业务代码的分析不方便贴出,随手写的 Demo 代码):

这篇文章我们再复习一下 Shallow SizeRetained Size 这两个概念,在此之前需要对 Java GC 机制有一定的了解,以前有记录过这块的内容,有兴趣可以参考看看 Java 垃圾回收机制。需要注意一下 GC Root,它们不会被 GC 回收,典型的 GC Root 就是静态变量,被 GC Root 直接 or 间接引用的对象也不会被回收。

Shallow Size

Shallow Size是指实例自身占用的内存,不包括它引用的其他实例。即:

Shallow Size = 类定义 + 属性占用空间 + 位数对齐

在我的 64 位机器上测试如下:

  • 类定义:声明一个类本身所需的空间,固定为 8 个字节。类定义空间不会重复计算,即使类继承了其他类,也只算 8 个字节。定义了一个没有任何属性的类,查看其 Shallow Size 大小为 8 个字节。
  • 属性占用空间:所有属性所占空间之和,包括自身的和父类的所有属性。属性分为基本类型和引用,如 int 类型占 4 个字节,long 类型占 8 个字节,引用固定 (String, Reference) 占 4 个字节。
  • 位数对齐:使总空间为 8 的倍数。比如某个类以上两项共 21 字节,那么为了对齐,会取最接近 8 的倍数的值,即它的 Shallow Size 是 24 个字节。与系统有关,有的不会对齐。

上面给出的截图,类结构如下,可以看出它就没有 位数对齐 这一项:

// Shallow Size = 28 = 8 + 4 + 8 + 4 + 4
// Retained Size = 36 = 28 + 8
data class Resource(
    val int: Int,
    val long: Long,
    val string: String,
    val reference: Res
)

// Shallow Size = 8
class Res()

Retained Size

Retained Size 是指某个实例被回收时,可以同时被回收的实例的 Shallow Size 之和。

因此在进行内存分析时,我们需要重点关注 Retained Size 较大的实例;另外也可以通过 Retained Size 判断出某个实例内部使用的实例是否被其他实例引用,比如说如果某个实例的 Retained Size 比较小,Shallow Size 比较大,说明它内部使用的某个实例还在其他地方被引用了(比如说对 Bitmap 实例而言,如果它的 Retained Size 很小,可以说明它内部的 byte 数组被另外的 Bitmap 实例复用了)。

举个栗子

现在有几个实例的引用关系如下图,假设每个实例的 Shallow Size 都为 X:

image.png

分别考虑回收这四个实例后,能释放的空间,即 Retained Size 大小,我们简单把某个实例 A 的 Retained Size 记作 R(A),Shallow Size 记作 S(A)。

  1. 移除 D 实例:D 没有引用任何实例,因此只会释放自身的 Shallow Size,即 R(D) = S(D) = X
  2. 移除 C 实例:移除 C 后,由于它引用了 D,且 D 没有被其他实例引用,因此 D 也会被一起回收,即 R(C) = S(C) + S(D) = 2X
  3. 移除 B 实例:移除 B 后,由于 B 和 A 实例都引用了 C 实例,所以移除 B 并不会让 C 实例被 GC 回收。即 R(B) = S(B) = X
  4. 移除 A 实例:B、C、D 实例被一起回收,即 R(A) = S(A) + S(B) + S(C) + S(D) = 4X

写在最后

在写这篇文章的时候翻了一些网上的博客,许多都是抄来抄去,或者说法各一,上面的数据经过了我自己的测试,发现 Shallow Size 具体大小的计算这里跟网上有些说法不太一致,有了解这块的同学可以评论区交流。

文中内容如有错误欢迎指出,共同进步!更新不易,觉得不错的留个再走哈~


Android视图系统:Android 视图系统相关的底层原理解析,看完定有收获。

Kotlin专栏:Kotlin 学习相关的博客,包括协程, Flow 等。

Android架构学习之路:架构不是一蹴而就的,希望我们有一天的时候,能够从自己写的代码中找到架构的成就感,而不是干几票就跑路。工作太忙,更新比较慢,大家有兴趣的话可以一起学习。

Android实战系列:记录实际开发中遇到和解决的一些问题。

Android优化系列:记录Android优化相关的文章,持续更新。