查看原文
其他

Android 内存优化的小知识

苍耳叔叔 鸿洋 2023-09-13

本文作者


作者:苍耳叔叔

链接:

https://juejin.cn/post/7259957160306131004

本文由作者授权发布。


1概述


最近在分析一个内存占用问题,就通过 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 Size 和 Retained Size 这两个概念,在此之前需要对 Java GC 机制有一定的了解,以前有记录过这块的内容,有兴趣可以参考看看 Java 垃圾回收机制。需要注意一下 GC Root,它们不会被 GC 回收,典型的 GC Root 就是静态变量,被 GC Root 直接 or 间接引用的对象也不会被回收。
https://ljd1996.github.io/2018/06/11/JVM%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/#%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6

2Shallow 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()
3Retained Size


Retained Size 是指某个实例被回收时,可以同时被回收的实例的 Shallow Size 之和。
因此在进行内存分析时,我们需要重点关注 Retained Size 较大的实例;另外也可以通过 Retained Size 判断出某个实例内部使用的实例是否被其他实例引用,比如说如果某个实例的 Retained Size 比较小,Shallow Size 比较大,说明它内部使用的某个实例还在其他地方被引用了(比如说对 Bitmap 实例而言,如果它的 Retained Size 很小,可以说明它内部的 byte 数组被另外的 Bitmap 实例复用了)。
举个栗子
现在有几个实例的引用关系如下图,假设每个实例的 Shallow Size 都为 X:
分别考虑回收这四个实例后,能释放的空间,即 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。

4写在最后


在写这篇文章的时候翻了一些网上的博客,许多都是抄来抄去,或者说法各一,上面的数据经过了我自己的测试,发现 Shallow Size 具体大小的计算这里跟网上有些说法不太一致,有了解这块的同学可以评论区交流。
文中内容如有错误欢迎指出,共同进步!更新不易,觉得不错的留个赞再走哈~


最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!


推荐阅读

Android实现特效或滤镜预览的几种方式
构建核心,Gradle Task 10 个知识点
Target SDK 升级到 29 AnimatorSet 动画不执行了


扫一扫 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~


┏(^0^)┛明天见!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存