前言
成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~。
本篇是 Android 内存优化的进阶篇,难度可以说达到了炼狱级别,建议对内存优化不是非常熟悉的仔细看看前篇文章:Android性能优化之内存优化,其中详细分析了以下几大模块:
1)、Android的内存管理机制
2)、优化内存的意义
3)、避免内存泄漏
4)、优化内存空间
5)、图片管理模块的设计与实现
如果你对以上基础内容都比较了解了,那么我们便开始 Android 内存优化的探索之旅吧。
大纲
直接进入正题,冲冲冲!!!
一、重识内存优化
Android给每个应用进程分配的内存都是非常有限的,那么,为什么不能把图片下载下来都放到磁盘中呢?那是因为放在内存中,展示会更 “快”,快的原因有两点,如下所示:
1)、硬件快:内存本身读取、存入速度快。
2)、复用快:解码成果有效保存,复用时,直接使用解码后对象,而不是再做一次图像解码。
这里说一下解码的概念。Android系统要在屏幕上展示图片的时候只认 “像素缓冲”,而这也是大多数操作系统的特征。而我们常见的jpg,png等图片格式,都是把 “像素缓冲” 使用不同的手段压缩后的结果,所以这些格式的图片,要在设备上展示,就必须经过一次解码,它的执行速度会受图片压缩比、尺寸等因素影响。(官方建议:把从内存中淘汰的图片,降低压缩比后存储到本地,以备后用,这样可以最大限度地降低以后复用时的解码开销。)
下面,我们来了解一下内存优化的一些重要概念。
1、手机RAM
手机不使用PC的DDR内存,采用的是LPDDR RAM,即 ”低功耗双倍数据速率内存“。其计算规则如下所示:
LPDDR系列的带宽 = 时钟频率 ✖️内存总线位数 / 8LPDDR4 = 1600MHZ ✖️64 / 8 ✖️双倍速率 = 25.6GB/s。复制代码
那么内存占用是否越少越好?
当系统内存充足的时候,我们可以多用一些获得更好的性能。当系统内存不足的时候,我们希望可以做到 ”用时分配,及时释放“。
2、内存优化的纬度
对于Android内存优化来说又可以细分为如下两个维度,如下所示:
1)、RAM优化
2)、ROM优化
1、RAM优化
主要是降低运行时内存。它的目的有如下三个:
1)、防止应用发生OOM。
2)、降低应用由于内存过大被LMK机制杀死的概率。
3)、避免不合理使用内存导致GC次数增多,从而导致应用发生卡顿。
2、ROM优化
降低应用占ROM的体积,进行APK瘦身。它的目的主要是为了降低应用占用空间,避免因ROM空间不足导致程序无法安装。
3、内存问题
那么,内存问题主要是有哪几类呢?内存问题通常来说,可以细分为如下三类:
1)、内存抖动
2)、内存泄漏
3)、内存溢出
下面,我们来了解下它们。
1、内存抖动
内存波动图形呈锯齿张、GC导致卡顿。
这个问题在Dalvik虚拟机上会更加明显,而ART虚拟机在内存管理跟回收策略上都做了大量优化,内存分配和GC效率相比提升了5~10倍,所以出现内存抖动的概率会小很多。
2、内存泄漏
Android系统虚拟机的垃圾回收是通过虚拟机GC机制来实现的。GC会选择一些还存活的对象作为内存遍历的根节点GC Roots,通过对GC Roots的可达性来判断是否需要回收。内存泄漏就是在当前应用周期内不再使用的对象被GC Roots引用,导致不能回收,使实际可使用内存变小。简言之,就是对象被持有导致无法释放或不能按照对象正常的生命周期进行释放。一般来说,可用内存减少、频繁GC,容易导致内存泄漏。
3、内存溢出
即OOM,OOM时会导致程序异常。Android设备出厂以后,java虚拟机对单个应用的最大内存分配就确定下来了,超出这个值就会OOM。单个应用可用的最大内存对应于 /system/build.prop 文件中的 dalvik.vm.heapgrowthlimit。
此外,除了因内存泄漏累积到一定程度导致OOM的情况以外,也有一次性申请很多内存,比如说一次创建大的数组或者是载入大的文件如图片的时候会导致OOM。而且,实际情况下很多OOM就是因图片处理不当而产生的。
二、常见工具选择
1、Memory Profiler
作用
1)、实时图表展示应用内存使用量。
2)、用于识别内存泄漏、抖动等。
3)、提供捕获堆转储、强制GC以及根据内存分配的能力。
优点
1)、方便直观
2)、线下使用
2、Memory Analyzer
强大的Java Heap分析工具,查找内存泄漏及内存占用,生成整体报告、分析内存问题等等。建议线下深入使用。
3、LeakCanary
自动化内存泄漏检测神器。建议仅用于线下集成。
它的缺点比较明显,具体有如下两点:
1)、虽然使用了idleHandler与多进程,但是dumphprof 的 SuspendAll Thread 的特性依然会导致应用卡顿。
2)、在三星等手机,系统会缓存最后一个Activity,此时应该采用更严格的检测模式。
三、Android内存管理机制回顾
ART 和 Dalvik 虚拟机使用分页和内存映射来管理内存。下面我们先从Java的内存分配开始说起。
1、Java 内存分配
Java的内存分配区域分为如下五部分:
1)、方法区:主要存放静态常量。
2)、虚拟机栈:Java变量引用。
3)、本地方法栈:native变量引用。
4)、堆:对象。
5)、程序计数器:计算当前线程的当前方法执行到多少行。
2、Java 内存回收算法
1、标记-清除算法
流程可简述为两步:
1)、标记所有需要回收的对象。
2)、统一回收所有被标记的对象。
优点
实现比较简单。
缺点
1)、标记、清除效率不高。
2)、产生大量内存碎片。
2、复制算法
流程可简述为三步:
1)、将内存划分为大小相等的两块。
2)、一块内存用完之后复制存活对象到另一块。
3)、清理另一块内存。
优点
实现简单,运行高效,每次仅需遍历标记一半的内存区域。
缺点
会浪费一半的空间,代价大。
3、标记-整理算法
流程可简述为三步:
1)、标记过程与 标记-清除算法 一样。
2)、存活对象往一端进行移动。
3)、清理其余内存。
优点
1)、避免 标记-清除 导致的内存碎片。
2)、避免复制算法的空间浪费。
4、分代收集算法
现在主流的虚拟机一般用的比较多的还是分代收集算法,它具有如下特点:
1)、结合多种算法优势。
2)、新生代对象存活率低,使用 复制算法。
3)、老年代对象存活率高,使用 标记-整理算法。
3、Android 内存管理机制
Android 中的内存是弹性分配的,分配值 与 最大值 受具体设备影响。
对于OOM场景其实可以细分为如下两种:
1)、内存真正不足。
2)、可用(被分配的)内存不足。
我们需要着重注意一下这两种的区分。
4、小结
以Android中虚拟机的角度来说,我们要清楚Dalvik 与 ART 区别,Dalvik仅固定一种回收算法,而ART回收算法可在运行期按需选择,并且,ART具备内存整理能力,减少内存空洞。
最后,LMK(Low Memory killer)机制保证了进程资源的合理利用,它的实现原理主要是根据进程分类和回收收益来综合决定的一套算法集。
作者:jsonchao