版权声明:本文为作者原创,转载必须注明出处。
转载请注明出处://www.greatytc.com/p/09922ab0390b
我们知道,从zygote孵化出来的进程都会记录在ActivityManagerService.mLruProcesses列表中,由ActivityManagerService进行统一管理,ActivityManagerService核心业务之一便是时时更新进程的状态,根据状态计算出进程对应的OomAdj值,这个值会传递到kernel中去,kernel有个低内存回收机制,在内存达到一定阀值时会触发清理OomAdj值高的进程,这边是Lowmemorykiller工作原理。
首先来看一下整体的流程:
我们都知道,AMS负责了系统中四大组件的启动、切换、调度以及应用进程管理和调度工作。在APP使用过程中,AMS会根据四大组件关键生命周期,在mLruProcesses中时时地设定对应进程的adj值(更新进程优先级),在内存低于lowmemorykiller杀进程的阈值时,lowmemorykiller会选择adj优先级最大(如果adj相等选择同adj中内存占用最大)的进程杀掉,释放内存。
其次,了解下定义在ProcessList.java文件的ADJ级别,oom_adj划分为16级,从-17到16之间取值。越大优先级越低
ADJ级别 | 取值 | 解释 |
---|---|---|
UNKNOWN_ADJ | 16 | 一般指将要会缓存进程,无法获取确定值 |
CACHED_APP_MAX_ADJ | 15 | 不可见进程的adj最大值 |
CACHED_APP_MIN_ADJ | 9 | 不可见进程的adj最小值 |
SERVICE_B_ADJ | 8 | B List中的Service(较老的、使用可能性更小) |
PREVIOUS_APP_ADJ | 7 | 上一个App的进程(往往通过按返回键) |
HOME_APP_ADJ | 6 | Home进程 |
SERVICE_ADJ | 5 | 服务进程(Service process) |
HEAVY_WEIGHT_APP_ADJ | 4 | 后台的重量级进程,system/rootdir/init.rc文件中设置 |
BACKUP_APP_ADJ | 3 | 备份进程 |
PERCEPTIBLE_APP_ADJ | 2 | 可感知进程,比如后台音乐播放 |
VISIBLE_APP_ADJ | 1 | 可见进程(Visible process) |
FOREGROUND_APP_ADJ | 0 | 前台进程(Foreground process) |
PERSISTENT_SERVICE_ADJ | -11 | 关联着系统或persistent进程 |
PERSISTENT_PROC_ADJ | -12 | 系统persistent进程,比如telephony |
SYSTEM_ADJ | -16 | 系统进程 |
NATIVE_ADJ | -17 | native进程(不被系统管理) |
然后,我们要了解两个问题:
1)阈值是怎么设定的?
AMS updateConfiguration方法是设定阈值的入口,在AMS初始化时执行一次,通过调用ProcessList中updateOomLevels方法,计算出阈值adj 和 minfree 并通过Lmkd.c写入文件中保存。
2)adj的更新流程是怎样的?
AMS调整进程的adj有3大护法:
- computeOomAdjLocked:计算adj(对优先级高于cache和empty的进程进行adj的分配)。该方法执行是在updateOomAdjLocked中。
- updateOomAdjLocked:更新adj(分配computeOomAdjLocked没有处理的cache和empty优先级的进程adj)
AMS updateOomAdjLocked方法的执行场景:
1) Activity的start/resume/finish;
2) Service的start/bind/unbind;
3) broadcast的分发/处理;
4) contentprovider的发布/移除/获取;
5) 进程的kill/attach等。
… - applyOomAdjLocked:应用adj,直接保存对应进程的adj:ProcessList执行setOomAdj方法,通过socket传送数据给Lmkd.c,最终Lmkd.c针对每一个进程创建单独文件并写入adj。该方法执行是在updateOomAdjLocked中,最终通过它把computeOomAdjLocked和updateOomAdjLocked计算好的adj更新并保存。
总结为如下表格:
AMS | ProcessList | lmkd.c | 描述 |
---|---|---|---|
applyOomAdjLocked | setOomAdj | LMK_PROCPRIO | 设置进程adj |
updateConfiguration | updateOomLevels | LMK_TARGET | 更新oom_adj |
cleanUpApplicationRecordLocked/handleAppDiedLocked | remove | LMK_PROCREMOVE | 移除进程 |
Lmkd.c | 对应方法 | 执行动作 |
---|---|---|
LMK_PROCPRIO | cmd_procprio | 写/proc/<pid>/oom_score_adj |
LMK_TARGET | cmd_target | 写/sys/module/lowmemorykiller/parameters/minfree写/sys/module/lowmemorykiller/parameters/adj |
LMK_PROCREMOVE | cmd_procremove | 删除/proc/<pid> |
查看系统阀值: adb shell cat /sys/module/lowmemorykiller/parameters/minfree
18432,23040,27648,32256,55296,80640
以上数字的单位是page. 1 page = 4 kb
对应的就是(MB): 72,90,108,216,216,315
为了更直观地了解执行流程,以applyOomAdjLocked过程为例,以下是时序图:
最终通过lowmemorykiller.c的核心方法:lowmen_scan执行杀进程:
监测到当前内存低于系统阈值(取出保存的minfree做对比),则会在大于等于该阈值的adj中寻找最大的(读取/proc/<pid>/oom_score_adj每个进程的adj值),出现相等的情况,则挑选内存占用最大的进程作为kill的目标,最终通过信号将其杀掉。
此文抛砖引玉,目的只是简单介绍lowmemorykiller的执行原理和流程,并没有贴代码也没有一行行地去抠代码细节,目的是梳理下框架,也是作为一个备忘的小总结。
想详细了解lowmemorykiller的朋友推荐看看以下博文:
http://gityuan.com/2016/09/17/android-lowmemorykiller/
http://blog.csdn.net/happylishang/article/details/54408733