导语智能手机发展到今天已经有十几个年头,手机的软硬件都已经发生了翻天覆地的变化,特别是 Android 阵营,从一开始的一两百 M 到今天动辄 4G,6G 内存。然而大部分的开发者观看下自己的异常上报系统,还是会发现各种内存问题仍然层出不穷,各种 OOM 为 crash 率贡献不少。Android 开发发展到今天也是已经比较成熟,各种新框架,新技术也是层出不穷,而内存优化一直都是 Android 开发过程一个不可避免的话题。 恰好最近做了内存优化相关的工作,这里也对 Android 内存优化相关的知识做下总结。
在开始文章之前推荐下公司同事翻译整理版本,因为篇幅有限这里我对一些内容只做简单总结,同时如果有不正确内容也麻烦帮忙指正。
本文将会对 Android 内存优化相关的知识进行总结以及最后案例分析(一二部分是理论知识总结,你也可以直接跳到第三部分看案例):
一、Android 内存分配回收机制
二、Android 常见内存问题和对应检测,解决方式。
三、JOOX 内存优化案例
四、总结
工欲善其事必先利其器,想要优化 App 的内存占用,那么还是需要先了解 Android 系统的内存分配和回收机制。
一、Android 内存分配回收机制参考 Android 操作系统的内存回收机制[1],这里简单做下总结:
从宏观角度上来看 Android 系统可以分为三个层次:
- Application Framework
- Dalvik 虚拟机
- Linux 内核。
这三个层次都有各自内存相关工作:
1. Application FrameworkAnroid 基于进程中运行的组件及其状态规定了默认的五个回收优先级:
![](http://oa5504rxk.bkt.clouddn.com/week38_AP/1.png)
- Empty process(空进程)
- Background process(后台进程)
- Service process(服务进程)
- Visible process(可见进程)
- Foreground process(前台进程)
系统需要进行内存回收时最先回收空进程,然后是后台进程,以此类推最后才会回收前台进程(一般情况下前台进程就是与用户交互的进程了,如果连前台进程都需要回收那么此时系统几乎不可用了)。
由此也衍生了很多进程保活的方法(提高优先级,互相唤醒,native 保活等等),出现了国内各种全家桶,甚至各种杀不死的进程。
Android 中由 ActivityManagerService 集中管理所有进程的内存资源分配。
2. Linux 内核![](http://oa5504rxk.bkt.clouddn.com/week38_AP/2.png)
参考阿里巴巴的 Android 内存优化分享[2],这里最简单的理解就是ActivityManagerService会对所有进程进行评分(存放在变量 adj 中),然后再讲这个评分更新到内核,由内核去完成真正的内存回收(lowmemorykiller, Oom_killer)。这里只是大概的流程,中间过程还是很复杂的,有兴趣的同学可以一起研究,代码在系统源码ActivityManagerService.java中。
3. Dalvik 虚拟机Android 进程的内存管理分析[3],对 Android 中进程内存的管理做了分析。
Android 中有 Native Heap 和 Dalvik Heap。Android 的 Native Heap 言理论上可分配的空间取决了硬件 RAM,而对于每个进程的 Dalvik Heap 都是有大小限制的,具体策略可以看看 android dalvik heap 浅析[4]。
Android App 为什么会 OOM 呢?其实就是申请的内存超过了 Dalvik Heap 的最大值。这里也诞生了一些比较”黑科技”的内存优化方案,比如将耗内存的操作放到 Native 层,或者使用分进程的方式突破每个进程的 Dalvik Heap 内存限制。
Android Dalvik Heap 与原生 Java 一样,将堆的内存空间分为三个区域,Young Generation,Old Generation, Permanent Generation。
![](http://oa5504rxk.bkt.clouddn.com/week38_AP/3.png)
最近分配的对象会存放在 Young Generation 区域,当这个对象在这个区域停留的时间达到一定程度,它会被移动到 Old Generation,最后累积一定时间再移动到 Permanent Generation 区域。系统会根据内存中不同的内存数据类型分别执行不同的 gc 操作。
GC 发生的时候,所有的线程都是会被暂停的。执行 GC 所占用的时间和它发生在哪一个 Generation 也有关系,Young Generation 中的每次 GC 操作时间是最短的,Old Generation 其次,Permanent Generation 最长。
GC 时会导致线程暂停,导致卡顿,Google 在新版本的 Android 中优化了这个问题, 在 ART 中对 GC 过程做了优化揭秘 ART 细节 —— Garbage collection[5],据说内存分配的效率提高了 10 倍,GC 的效率提高了 2-3 倍(可见原来效率有多低),不过主要还是优化中断和阻塞的时间,频繁的 GC 还是会导致卡顿。
上面就是 Android 系统内存分配和回收相关知识,回过头来看,现在各种手机厂商鼓吹人工智能手机,号称 18 个月不卡顿,越用越快,其实很大一部分 Android 系统的内存优化有关,无非就是利用一些比较成熟的基于统计,机器学习的算法定时清理数据,清理内存,甚至提前加载数据到内存。 |