这一篇博客,我们来分析一下AMS进程管理流程中,负责计算进程oom_adj值的computeOomAdjLocked函数。
从难度上来讲,computeOomAdjLocked函数比updateOomAdjLocked函数简单,因为它的职责更明确和单一。
然而,由于Android定义的oom_adj种类庞杂,使得这个函数的分支很多,细节显得极其的繁琐。
因此从功利的角度来看,大家知道这个函数的用途和大概脉络即可。
不过对于一个框架工程师而言,阅读源码的耐心可能比写代码的能力更重要,因此我们还是耐着性子将代码看完。
“RTFSC”,毕竟大神是这么告诉我们的。
computeOomAdjLocked函数的代码很长,
因此在这篇博客中,我们还分段进行研究,然后试着进行总结。
一、computeOomAdjLocked Part-I
private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
boolean doingAll, long now) {
//之前的博客中提到过,updateOomAdjLocked函数每次更新oom_adj时,都会分配一个序号
//此处就是根据序号判断是否已经处理过命令
if (mAdjSeq == app.adjSeq) {
// This adjustment has already been computed.
return app.curRawAdj;
}
//ProcessRecord对应的ActivityThread不存在了
//修改其中的一些变量,此时的oom_adj为CACHED_APP_MAX_ADJ,
//其意义我们在前一篇博客中已经提到过
if (app.thread == null) {
app.adjSeq = mAdjSeq;
app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
}
//初始化一些变量
//这些变量的具体用途,在篇博客中我们不关注
//大家只用留意一下ProcessRecord的schedGroup、procState和oom_adj即可
app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
app.adjSource = null;
app.adjTarget = null;
app.empty = false;
app.cached = false;
final int activitiesSize = app.activities.size();
//这个判断没啥意义,ProcessRecord中只有初始化时为maxAdj赋值
//maxAdj取值为UNKNOWN_ADJ,即最大的1001
if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
//这部分代码就是修改app的curSchedGroup,并将oom_adj设置为maxAdj
//实际过程中,应该是不会执行的的
......................
}
//保存当前TOP Activity的状态
final int PROCESS_STATE_CUR_TOP = mTopProcessState;
......................
}
以上代码就是computeOomAdjLocked函数的第一部分。
从代码不难看出,这部分内容的主要目的是:
1、根据参数及进程的状态,决定是否需要进行后续的计算;
2、初始化一些变量。
二、computeOomAdjLocked Part-II
在第二部分,computeOomAdjLocked开始干“正事儿”了:
.................
// Determine the importance of the process, starting with most
// important to least, and assign an appropriate OOM adjustment.
// 上面的这段注释为整个computeOomAdjLocked函数“代言”
int adj;
int schedGroup;
int procState;
boolean foregroundActivities = false;
BroadcastQueue queue;
//若进程包含正在前台显示的Activity
if (app == TOP_APP) {
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
//单独的一种schedGroup
schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
app.adjType = "top-activity";
//当前处理的是包含前台Activity的进程时,才会将该值置为true
foregroundActivities = true;
procState = PROCESS_STATE_CUR_TOP;
} else if (app.instrumentationClass != null) {
//处理正在进行测试的进程
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.adjType = "instrumentation";
procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
} else if ((queue = isReceivingBroadcast(app)) != null) {
//处理正在处理广播的进程
// An app that is currently receiving a broadcast also
// counts as being in the foreground for OOM killer purposes.
// It's placed in a sched group based on the nature of the
// broadcast as reflected by which queue it's active in.
adj = ProcessList.FOREGROUND_APP_ADJ;
//根据处理广播的Queue,决定调度策略
schedGroup = (queue == mFgBroadcastQueue)
? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
app.adjType = "broadcast";
procState = ActivityManager.PROCESS_STATE_RECEIVER;
} else if (app.executingServices.size() > 0) {
//处理Service正在运行的进程
// An app that is currently executing a service callback also
// counts as being in the foreground.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = app.execServicesFg ?
ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND;
procState = ActivityManager.PROCESS_STATE_SERVICE;
} else {
//其它进程,在后续过程中再进一步处理
// As far as we know the process is empty. We may change our mind later.
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
// At this point we don't actually know the adjustment. Use the cached adj
// value that the caller wants us to.
// 先将adj临时赋值为cachedAdj,即参数传入的UNKNOW_ADJ
adj = cachedAdj;
procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
app.cached = true;
app.empty = true;
app.adjType = "cch-empty";
}
..................
以上代码可以看作是computeOomAdjLocked的第二部分,从这部分代码可以看出:
1、包含前台Activity的进程、运行测试类的进程、处理广播的进程及包含正在运行服务的进程,
其oom_adj均被赋值为FOREGROUND_APP_ADJ,即从LMK的角度来看,它们的重要性是一致的。
但这些进程的procState不同,于是从AMS主动回收内存的角度来看,它们的重要性不同。
此外,这些进程的schedGroup不同。
之前的博客分析过,Process.java中提供了接口,可以调用Linux提供的接口函数设置schedGroup,使得进程具有不同的调度策略。
从获取CPU资源的能力来看,SCHED_GROUP_TOP_APP应该强于SCHED_GROUP_DEFAULT,
最后才轮到SCHED_GROUP_BACKGROUND。
2、对于其它种类的进程,这部分代码先将它们的oom_adj设置为UNKNOW_ADJ,
proc_state置为PROCESS_STATE_CACHED_EMPTY,在后续流程中再作进一步处理。
三、computeOomAdjLocked Part-III
这一部分代码主要处理包含Activity,但是Activity不在前台的进程。
注意到这些进程包括之前提到的正在处理广播、服务或测试的进程,以及oom_adj暂时为UNKNOW_ADJ的进程。
不过只有UNKNOW_ADJ对应的进程,才有可能进行实际的更新。
..................
// Examine all activities if not already foreground.
if (!foregroundActivities && activitiesSize > 0) {
//之前分析updateOomAdjLocked的第一部分时,简单提到过rankTaskLayersIfNeeded函数
//该函数会更新包含Activity的Task的rankLayer
//按照显示层次从上到下,rankLayer逐渐增加,对应的最大值就是VISIBLE_APP_LAYER_MAX
int minLayer = ProcessList.VISIBLE_APP_LAYER_MAX;
//依次轮询进程中的Activity
for (int j = 0; j < activitiesSize; j++) {
final ActivityRecord r = app.activities.get(j);
...................
//如果进程包含可见Activity,即该进程是个可见进程
if (r.visible) {