论文地址:http://www.it.iitb.ac.in/frg/wiki/images/f/f4/113050076_Rajesh_Prodduturi_Stage-01_report_8_113050076_stage01.pdf
近期在Umeng上面看到非常多的OOM错误报告,虽然现在还没能完全解决这个问题,但是在解决的过程中读到了国外的一篇论文。看完之后对Android内存管理有了新的理解,心得如下。
一、Android的内存管理
先谈一下Android造物主在它诞生之初的设定:
Low kernel size
Low size standard C lib
缺陷相当明显:
没有内存保护,会引起潜在的冲突
没有虚拟内存
虽然Android是基于DVM的实现,Dalvik runtime提供了内存回收机制,但是并不意味着你可以忽略内存管理。
二、Linux的内存管理
Linux的内存管理是一门比较复杂的话题,有熟悉的朋友可以来给我们讲一下,这里只简单涉及一下一些简单知识和Linux的OOM Killer机制。
逻辑地址:段地址和位移来进行定位
虚拟地址:每个进程都会有一个用户地址和虚拟地址空间(就是它)
物理地址:代表了真实的RAM,就是大家经常说的几个G内存代表了它的最大寻址空间。
Paging & Segmentation:Linux倾向于使用Paging而不是Segmentation,因为他们两者是冗余的,RISC指令集对Segmentation的支持是有限的,而在我们的移动操作系统里面当然是基本只支持Paging。它们是用来做逻辑地址、虚拟地址到物理地址的转义用的,当我们程序在调用逻辑地址和虚拟地址的时候,需要访问它们拿到真实的物理地址(这是移动终端,一般的操作系统是逻辑地址通过段机制解析为虚拟地址,然后虚拟地址再通过分页解析为物理地址,但是移动终端认为段机制是冗余的,所以直接就对逻辑地址或者虚拟地址进行分页来解析了)。
Linux的OOM Killer是大家经常打交道的一个Linux保护机制,它主要用于杀掉一些在系统中某些进程大量请求内存导致系统内存不足(Low memory)而进行自我保护的机制。OOM会在这种情况触发杀掉某个进程(挑选的算法可以去查看oom_killer.c,挑选的算法很简单朴实,就是选择表现最差的那个)以腾出内存留给系统,不至于让系统崩溃,就是我们老话说的:弃卒保车。
三、Low Memory Scenarios
进程在运行时都是不停的申请和释放内存的过程,每次申请内存时,操作系统底层都会调用_alloc_pages(),如果发现内存不够就会调用_try_to_free_pages(),如果后面一个方法调用失败了,就会调用out_of_memory()了。
操作系统会为每个进程打分,这个时候那个被操作系统认为是“坏”进程的倒霉蛋就得被操作系统结果了。
等等,也许你会发现有什么不对劲了,有的进程它不得不占用很多内存,而且它似乎对用户更重要,或者它现在因为用户的访问对内存的操作很频繁,操作系统凭什么觉得它是“坏”的就干掉了呢?
所以Android就引入了一个区别于Linux的OOM Killer的优化版,它对进程进行了优先级的区分和分组。
Android的进程分组:
前台进程(就是你正在看的界面)
和前台进程绑定的进程(bindservice)
正在运行的服务进程
隐藏的进程(对用户不可见)
Content Provider
空进程
Android为这些进程进行分组的目的是为了对于不同进程的OOM事件进行不同的处理。Android为这些进程规定了一个阀值,当这些进程占用的内存数量超过这个阀值一样会被消灭。这些阀值视设备厂商自己的设备而定。
一般1-6的分组阀值大小:1<2<3<4<5<6。也就是说抛开其他原因,单就阀值来看就是说前台进程的OOM几率最大!
好了,问题来了,如果我们要解决OOM问题,应该怎么做呢?
耗费内存的操作如果能放到后台进程去做就交给后台进程
对于一些空进程虽然他们在OOM事件中处于不同的分组,但是他们也是导致OOM的来源(因为他们的阀值最大,系统允许分配的内存最多),所以当onLowMemory发生的时候应当先去清理一下这些进程。
尽量使用WeakReference,当系统调用onLowMemory的时候去clear一下这部分内存,或许还有转圜的余地。