关于Android系统的内存回收机制,相信大家都不陌生,Android基于各个应用进程承载四大组件的状态对应用进程进行重要性评估,并在系统内存紧张时根据重要性由低到高来选择杀死应用进程,以达到释放内存的目的。重要性评估由AMS执行,具体来说就是AMS.updateOomAdjLocked函数,反过来说,AMS.updateOomAdjLocked的作用就是更新应用进程的重要性。
应用进程(ProcessRecord)的重要性由三个状态值表示:
- adj:LMK杀进程的评分依据
- procState:指示进程状态
- schedGroup:指示进程调度策略
本文不会分析该函数的具体执行,而是讨论这三个状态值之间的差异,代码参考Android N。
基本的思考
既然要评估进程的重要性,并以此作为LMK回收进程的依据,理论上来讲由单个状态来指示重要性,并告知LMK应该是最好的方案,简单粗暴,逻辑清晰。大家从很多地方都可以得知,oom_adj就是提供给LMK进行内存回收的依据,但是对于procState和schedGroup要么一笔带过,要么干脆不提及。但是既然存在这么两个东西,必然有其存在的意义。
我们先大概讲下AMS.updateOomAdjLocked的执行流程:
updateOomAdjLocked(ProcessRecord app)
-updateOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord TOP_APP, boolean doingAll, long now)
-computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
boolean doingAll, long now)
-applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
long nowElapsed)
updateOomAdjLocked()
-computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
boolean doingAll, long now)
-applyOomAdjLocked(ProcessRecord app, boolean doingAll, long now,
long nowElapsed)
有上述两种执行路径,相同的空格缩进表示函数调用栈的同一级,注意updateOomAdjLocked有多个重载版本。重点说明出下面几点:
- updateOomAdjLocked在应用进程的组件运行状态发生改变时被调用,比如有Service启动,有广播接收者收到广播,有Activity启动等,这很好理解,因为进程重要性的计算就依赖于组件运行状态,既然组件运行状态发生了改变,就应该实时更新;
- computeOomAdjLocked根据一定规则计算出三个状态值,这个规则跟Android将进程划分的5个优先级有关系,即前台进程、可见进程、服务进程、后台进程、空进程,这里不详细说明;
- applyOomAdjLocked将computeOomAdjLocked计算出的三个状态值应用起来,即真正发挥这三个状态值的作用。
既然要分析这三个状态值的作用,看源码如何使用这些值就是最好的办法,从上面可以知道,applyOomAdjLocked是我们的切入点。
applyOomAdjLocked
下面代码中涉及的ProcessRecord中cur开头的字段均为computeOomAdjLocked函数计算出的当前状态值,比如curAdj,curProcState,curSchedGroup,下面从代码的角度分别描述它们的作用。
adj
if (app.curAdj != app.setAdj) {
ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj);
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
"Set " + app.pid + " " + app.processName + " adj " + app.curAdj + ": "
+ app.adjType);
app.setAdj = app.curAdj;
app.verifiedAdj = ProcessList.INVALID_ADJ;
}
curAdj是computeOomAdjLocked计算出的adj值,赋值给setAdj,并且调用ProcessList.setOomAdj,继续往下看:
public static final void setOomAdj(int pid, int uid, int amt) {
if (amt == UNKNOWN_ADJ)
return;
long start = SystemClock.elapsedRealtime();
ByteBuffer buf = ByteBuffer.allocate(4 *