3、Android 内存管理机制
Android 中的内存是 弹性分配 的,分配值 与 最大值 受具体设备影响。
对于 OOM场景 其实可以细分为如下两种:
- 1)、内存真正不足。
- 2)、可用(被分配的)内存不足。
我们需要着重注意一下这两种的区分。
4、小结
以Android中虚拟机的角度来说,我们要清楚 Dalvik 与 ART 区别,Dalvik 仅固定一种回收算法,而 ART 回收算法可在 运行期按需选择,并且,ART 具备 内存整理 能力,减少内存空洞。
最后,LMK(Low Memory killer) 机制保证了进程资源的合理利用,它的实现原理主要是 根据进程分类和回收收益来综合决定的一套算法集。
四、内存抖动
当 内存频繁分配和回收 导致内存 不稳定,就会出现内存抖动,它通常表现为 频繁GC、内存曲线呈锯齿状。
并且,它的危害也很严重,通常会导致 页面卡顿,甚至造成 OOM。
1、那么,为什么内存抖动会导致 OOM?
主要原因有如下两点:
- 1)、频繁创建对象,导致内存不足及碎片(不连续)。
- 2)、不连续的内存片无法被分配,导致OOM。
2、内存抖动解决实战
这里我们假设有这样一个场景:点击按钮使用 handler 发送一个空消息,handler 的 handleMessage 接收到消息后创建内存抖动,即在 for 循环创建 100个容量为10万 的 strings 数组并在 30ms 后继续发送空消息。
一般使用 Memory Profiler (表现为 频繁GC、内存曲线呈锯齿状)结合代码排查即可找到内存抖动出现的地方。
通常的技巧就是着重查看 循环或频繁被调用 的地方。
3、内存抖动常见案例
下面列举一些导致内存抖动的常见案例,如下所示:
1、字符串使用加号拼接
- 1)、使用StringBuilder替代。
- 2)、初始化时设置容量,减少StringBuilder的扩容。
2、资源复用
- 1)、使用 全局缓存池,以 重用频繁申请和释放的对象。
- 2)、注意 结束 使用后,需要 手动释放对象池中的对象。
3、减少不合理的对象创建
- 1)、ondraw、getView 中创建的对象尽量进行复用。
- 2)、避免在循环中不断创建局部变量。
4、使用合理的数据结构
使用 SparseArray类族、ArrayMap 来替代 HashMap。
五、内存优化体系化搭建
在开始我们今天正式的主题之前,我们先来回归一下内存泄漏的概念与解决技巧。
所谓的内存泄漏就是 内存中存在已经没有用的对象。它的 表现 一般为 内存抖动、可用内存逐渐减少。 它的 危害 即会导致 内存不足、GC频繁、OOM。
而对于 内存泄漏的分析 一般可简述为如下 两步:
- 1)、使用 Memory Profiler 初步观察。
- 2)、通过 Memory Analyzer 结合代码确认。
1、MAT回顾
MAT查找内存泄漏
对于MAT来说,其常规的查找内存泄漏的方式可以细分为如下三步:
- 1)、首先,找到当前 Activity,在 Histogram 中选择其 List Objects 中的 with incoming reference(哪些引用引向了我)。
- 2)、然后,选择当前的一个 Path to GC Roots/Merge to GC Roots 的 exclude All 弱软虚引用。
- 3)、最后,找到的泄漏对象在左下角下会有一个小圆圈。
此外,在 Android性能优化之内存优化 还有几种进阶的使用方式,这里就不一一赘述了,下面,我们来看看关于 MAT 使用时的一些关键细节。
MAT的关键使用细节
要全面掌握MAT的用法,必须要先了解 隐藏在 MAT 使用中的四大细节,如下所示:
- 1)、善于使用 Regex 查找对应泄漏类。
- 2)、使用 group by package 查找对应包下的具体类。
- 3)、明白 with outgoing references 和 with incoming references 的区别。
- with outgoing references:它引用了哪些对象。
- with incoming references:哪些对象引用了它。
- 4)、了解 Shallow Heap 和 Retained Heap 的区别。
- Shallow Heap:表示对象自身占用的内存。
- Retained Heap:对象自身占用的内存 + 对象引用的对象所占用的内存。
MAT 关键组件回顾
除此之外,MAT 共有 5个关键组件 帮助我们去分析内存方面的问题,分别如下所示:
- 1)、Dominator_tree
- 2)、Histogram
- 3)、thread_overview
- 4)、Top Consumers
- 5)、Leak Suspects
下面我们这里再简单地回顾一下它们。
1、Dominator(支配者):
如果从GC Root到达对象A的路径上必须经过对象B,那么B就是A的支配者。
2、Histogram和dominator_tree的区别:
- 1)、Histogram 显示 Shallow Heap、Retained Heap、Objects,而 dominator_tree 显示的是 Shallow Heap、Retained Heap、Percentage。
- 2)、Histogram 基于 类 的角度,dominator_tree是基于 实例 的角度。Histogram 不会具体显示每一个泄漏的对象,而dominator_tree会。
3、thread_overview
查看 线程数量 和 线程的 Shallow Heap、Retained Heap、Context Class Loader 与 is Daemon。
4、Top Consumers
通过 图形 的形式列出 占用内存比较多的对象。
在下方的 Biggest Objects 还可以查看其 相对比较详细的信息,例如 Shallow Heap、Retained Heap。
5、Leak Suspects
列出有内存泄漏的地方,点击 Details 可以查看其产生内存泄漏的引用链。
2、搭建体系化的图片优化 / 监控机制
在介绍图片监控体系的搭建之前,首先我们来回顾下 Android Bitmap 内存分配的变化。
Android Bitmap 内存分配的变化
在Android 3.0之前
- 1)、Bitmap 对象存放在 Java Heap,而像素数据是存放在 Native 内存中的。
- 2)、如果不手动调用 recycle,Bitmap Native 内存的回收完全依赖 finalize 函数回调,但是回调时机是不可控的。
Android 3.0 ~ Android 7.0
将 Bitmap对象 和 像素数据 统一放到 Java Heap 中,即使不调用 recycle,Bitmap 像素数据也会随着对象一起被回收。
但是,Bitmap 全部放在 Java Heap 中的缺点很明显,大致有如下两点:
- 1)、Bitmap是内存消耗的大户,而 Max Java Heap 一般限制为 256、512MB,Bitmap 过大过多容易导致 OOM。
- 2)