12.2.2 OOM adj的计算过程

OOM adj的计算过程由computeOomAdjLocked()方法完成,该方法的执行流程非常复杂,这里将其分成八个阶段,逐步分析。

1.computeOomAdjLocked()第一阶段

computeOomAdjLocked()第一阶段的主要工作如下:

如果已经计算过该进程的adj值,则将该进程的curAdj、curRawAdj、nonStoppingAdj全部赋值为参数hiddenAdj指定的值,并返回该值。

如果ApplicationThread为null,则将该进程的curAdj赋值为HIDDEN_APP_MAX_ADJ, curSchedGroup赋值为THREAD_GROUP_BG_NONINTERACTIVE,并返回curAdj。

如果进程的maxAdj小于FOREGROUND_APP_ADJ,则将该进程的curRawAdj、nonStoppingAdj、curAdj全部赋值为maxAdj。同时将该进程的adjType赋值为fixed, foregroundActivities赋值为false, keeping赋值为true, curSchedGroup赋值为THREAD_GROUP_DEFAULT。最后返回curAdj。

代码如下:


private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,

ProcessRecord TOP_APP, boolean recursed, boolean doingAll){

//adj序列号重复,说明已经计算过adj值

if(mAdjSeq==app.adjSeq){

if(!recursed&&app.hidden){

app.curAdj=app.curRawAdj=app.nonStoppingAdj=hiddenAdj;

}

return app.curRawAdj;//返回当前初始adj值

}

//ApplicationThread为null时,例如还未启动应用程序主线程时

if(app.thread==null){

app.adjSeq=mAdjSeq;//adj序列号赋值为当前序列号

//设置进程的调度组为“后台非交互”,占用的CPU时间片较少

app.curSchedGroup=Process.THREAD_GROUP_BG_NONINTERACTIVE;

//设置该进程的当前adj值为隐藏应用程序进程的最大adj值

return(app.curAdj=ProcessList.HIDDEN_APP_MAX_ADJ);

}

app.adjTypeCode=ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;

//默认adj类型代码

app.adjSource=null;

app.adjTarget=null;

app.empty=false;//非empty后台进程

app.hidden=false;//非隐藏进程

//计算应用程序进程中运行的Activity数量

final int activitiesSize=app.activities.size();

if(app.maxAdj<=ProcessList.FOREGROUND_APP_ADJ){

app.adjType="fixed";//固定类型,其adj值固定为maxAdj

app.adjSeq=mAdjSeq;

//初始adj,不计算stopping状态Activity的adj值,最大adj值

app.curRawAdj=app.nonStoppingAdj=app.maxAdj;

//该进程是否有前台Activity在运行

app.foregroundActivities=false;

app.keeping=true;//正在运行代码,需要保留该进程

//设置默认调度组

app.curSchedGroup=Process.THREAD_GROUP_DEFAULT;

//system进程是否正在显示UI,该值与system进程整理内存有关

app.systemNoUi=true;

//如果进程运行了Top Activity,则系统进程不显示UI

if(app==TOP_APP){

app.systemNoUi=false;

}else if(activitiesSize>0){

for(int j=0;j<activitiesSize;j++){

final ActivityRecord r=app.activities.get(j);

//如果进程运行了可见Activity,则系统进程不显示UI

if(r.visible){

app.systemNoUi=false;

break;

}

}

}

return(app.curAdj=app.maxAdj);//当前adj值固定为maxAdj

}


2.computeOomAdjLocked()第二阶段

computeOomAdjLocked()方法第二阶段主要工作是根据应用程序组件的类型,将应用程序进程分为前台进程和后台进程。代码如下:


private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,

ProcessRecord TOP_APP, boolean recursed, boolean doingAll){

……//省略第一阶段内容

app.keeping=false;

app.systemNoUi=false;

int adj;

int schedGroup;

boolean foregroundActivities=false;

boolean interesting=false;

BroadcastQueue queue;

if(app==TOP_APP){

//为运行了Top Activity的应用程序设置adj信息

adj=ProcessList.FOREGROUND_APP_ADJ;

schedGroup=Process.THREAD_GROUP_DEFAULT;

app.adjType="top-activity";//前台Top Activity

foregroundActivities=true;//拥有前台Activity

interesting=true;

}else if(app.instrumentationClass!=null){

//为运行了Instrumentation的应用程序设置adj信息

adj=ProcessList.FOREGROUND_APP_ADJ;

schedGroup=Process.THREAD_GROUP_DEFAULT;

app.adjType="instrumentation";//前台运行Instrumentation

interesting=true;

}else if((queue=isReceivingBroadcast(app))!=null){

//为正在接收并处理广播的应用程序设置adj信息

adj=ProcessList.FOREGROUND_APP_ADJ;

schedGroup=(queue==mFgBroadcastQueue)

?Process.THREAD_GROUP_DEFAULT:

Process.THREAD_GROUP_BG_NONINTERACTIVE;

app.adjType="broadcast";//前台处理Broadcast

}else if(app.executingServices.size()>0){

//为正在回调应用程序Service生命周期方法的应用程序设置adj信息

adj=ProcessList.FOREGROUND_APP_ADJ;

schedGroup=Process.THREAD_GROUP_DEFAULT;

app.adjType="exec-service";//前台执行Service

}else if(activitiesSize>0){

//为由后台运行(如paused状态)Activity的应用程序设置adj信息

adj=hiddenAdj;

schedGroup=Process.THREAD_GROUP_BG_NONINTERACTIVE;

app.hidden=true;

app.adjType="bg-activities";//后台暂停进程

}else{//其他情况下视为后台空进程

adj=hiddenAdj;

schedGroup=Process.THREAD_GROUP_BG_NONINTERACTIVE;

app.hidden=true;

app.empty=true;

app.adjType="bg-empty";//后台空进程

}


运行TOP_APP、执行Instrumentation、处理Broadcast、执行Service的进程称为前台进程,其他进程称为后台进程。前台应用程序进程的adj赋值为FOREGROUND_APP_ADJ,后台应用程序进程的adj赋值为hiddenAdj。其中前台进程和后台进程又根据adjType分为不同的子类型,如上述代码所示。第二阶段只是对adj进行粗略划分,后续可能对其adj值予以调整。

3.computeOomAdjLocked()第三阶段

computeOomAdjLocked()第三阶段主要工作是细分和调整第二阶段划分的adj值,并从前台进程和后台进程中细分出hidden为false的非隐藏进程。代码如下:


private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,

ProcessRecord TOP_APP, boolean recursed, boolean doingAll){

……//省略前两个阶段

boolean hasStoppingActivities=false;

//在第二阶段,只有TOP_APP才会将foregroundActivities赋值为true

if(!foregroundActivities&&activitiesSize>0){

for(int j=0;j<activitiesSize;j++){

final ActivityRecord r=app.activities.get(j);

if(r.visible){//有可见但非前台的Activity

if(adj>ProcessList.VISIBLE_APP_ADJ){

//将adj调整为VISIBLE_APP_ADJ

adj=ProcessList.VISIBLE_APP_ADJ;

app.adjType="visible";//调整为visible

}

schedGroup=Process.THREAD_GROUP_DEFAULT;

app.hidden=false;//有可见Activity,对应进程非hidden

foregroundActivities=true;//调整为true

break;

}else if(r.state==ActivityState.PAUSING||

r.state==ActivityState.PAUSED){

//有处于Pausing和Paused状态的Activity,调整其adj

if(adj>ProcessList.PERCEPTIBLE_APP_ADJ){

adj=ProcessList.PERCEPTIBLE_APP_ADJ;

app.adjType="pausing";

}

app.hidden=false;

foregroundActivities=true;

}else if(r.state==ActivityState.STOPPING){

//有Stopping状态的Activity

app.hidden=false;

foregroundActivities=true;

hasStoppingActivities=true;

}

}

}

if(adj>ProcessList.PERCEPTIBLE_APP_ADJ){

if(app.foregroundServices){

adj=ProcessList.PERCEPTIBLE_APP_ADJ;

app.hidden=false;

app.adjType="foreground-service";

schedGroup=Process.THREAD_GROUP_DEFAULT;

}else if(app.forcingToForeground!=null){

//调用了AMS.setProcessForeground将进程强制切换到前台时

adj=ProcessList.PERCEPTIBLE_APP_ADJ;

app.hidden=false;

app.adjType="force-foreground";

app.adjSource=app.forcingToForeground;

schedGroup=Process.THREAD_GROUP_DEFAULT;

}

}

if(app.foregroundServices){

interesting=true;

}


在第三阶段,满足以下条件的进程视为非隐藏进程:

有可见但非前台的Activity。

有可见Activity,该进程有未被完全覆盖的Activity,自然是非隐藏进程。

有处于Pausing和Paused状态的Activity。当Activity处于Pausing状态时,其onPause方法未执行或未执行完毕,依然可能是当前正在显示的Activity。当Activity为Paused状态,即已经执行完onPause方法,虽然该Activity不再显示,但其仍然可能被用户感知,比如后台运行的音乐播放器。因此这两种情况均暂时视为非隐藏进程。

有处于STOPPING状态的Activity。有正在处于停止状态的Activity,也暂时视为非隐藏进程,因为该Activity随时可能被onRestart。

调用了AMS.setProcessForeground将进程强制切换到前台。调用setProcessForeground可以强制将一个进程切换到前台,因此视为非隐藏进程。

非隐藏进程并非只有以上几类,后续还会遇到。接下来分析computeOomAdjLocked第四阶段。

4.computeOomAdjLocked()第四阶段

computeOomAdjLocked()第四阶段只是调整四类特殊进程的adj值,这四类特殊进程也被视为非隐藏进程。虽然为这四类特殊进程分配了特殊的adj值,但其adj值在后续阶段仍然可能被修改。代码如下:


private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,

ProcessRecord TOP_APP, boolean recursed, boolean doingAll){

……//省略前三个阶段

if(adj>ProcessList.HEAVY_WEIGHT_APP_ADJ&&app==mHeavyWeightProcess){

//调整heavy-weight进程的adj为指定值

adj=ProcessList.HEAVY_WEIGHT_APP_ADJ;

schedGroup=Process.THREAD_GROUP_BG_NONINTERACTIVE;

app.hidden=false;//也是非隐藏进程

app.adjType="heavy";

}

if(adj>ProcessList.HOME_APP_ADJ&&app==mHomeProcess){

//调整Home进程的adj为指定值

adj=ProcessList.HOME_APP_ADJ;

schedGroup=Process.THREAD_GROUP_BG_NONINTERACTIVE;

app.hidden=false;//也是非隐藏进程

app.adjType="home";

}

if(adj>ProcessList.PREVIOUS_APP_ADJ&&app==mPreviousProcess

&&app.activities.size()>0){

//调整上一个应用程序的adj

adj=ProcessList.PREVIOUS_APP_ADJ;

schedGroup=Process.THREAD_GROUP_BG_NONINTERACTIVE;

app.hidden=false;//非隐藏进程

app.adjType="previous";

}

app.adjSeq=mAdjSeq;

app.curRawAdj=app.nonStoppingAdj=adj;

if(mBackupTarget!=null&&app==mBackupTarget.app){

//调整backup进程的adj

if(adj>ProcessList.BACKUP_APP_ADJ){

adj=ProcessList.BACKUP_APP_ADJ;

app.adjType="backup";

app.hidden=false;//非隐藏进程

}

}


调整四类特殊进程的adj值后,开始执行computeOomAdjLocked()第五阶段的工作。

5.computeOomAdjLocked()第五阶段

computeOomAdjLocked()第五阶段主要工作是,根据进程中拥有的应用程序Service的启动状态和绑定标记调整进程的adj值。代码如下:


private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,

ProcessRecord TOP_APP, boolean recursed, boolean doingAll){

……//省略前四个阶段

//如果非前台进程,并且进程中包含Service

if(app.services.size()!=0&&(adj>ProcessList.FOREGROUND_APP_ADJ

||schedGroup==Process.THREAD_GROUP_BG_NONINTERACTIVE)){

final long now=SystemClock.uptimeMillis();

Iterator<ServiceRecord>jt=app.services.iterator();

while(jt.hasNext()&&adj>ProcessList.FOREGROUND_APP_ADJ){

ServiceRecord s=jt.next();//应用程序Service运行时的表示形式

if(s.startRequested){//该Service被启动过

//该进程启动后显示过UI,并且不是Home进程

if(app.hasShownUi&&app!=mHomeProcess){

if(adj>ProcessList.SERVICE_ADJ){

app.adjType="started-bg-ui-services";

}

}else{

//如果Service未激活时间低于其最大等待时间(30s)

if(now<(s.lastActivity+MAX_SERVICE_INACTIVITY)){

if(adj>ProcessList.SERVICE_ADJ){

adj=ProcessList.SERVICE_ADJ;//调整adj

app.adjType="started-services";

app.hidden=false;//非隐藏进程

}

}

if(adj>ProcessList.SERVICE_ADJ){

app.adjType="started-bg-services";

}

}

app.keeping=true;//保留该进程,不“杀死”

}

//至此,if(s.startRequested)分支结束

if(s.connections.size()>0&&(……)){

Iterator<ArrayList<ConnectionRecord>>kt

=s.connections.values().iterator();

while(kt.hasNext()&&

adj>ProcessList.FOREGROUND_APP_ADJ){

ArrayList<ConnectionRecord>clist=kt.next();

for(int i=0;i<clist.size()&&

adj>ProcessList.FOREGROUND_APP_ADJ;i++){

ConnectionRecord cr=clist.get(i);

……//如果客户端是该进程本身,则略过该客户端

/*BIND_WAIVE_PRIORITY是调用bindService方法

绑定已启动Service时传入的启动参数/

if((cr.flags&Context.BIND_WAIVE_PRIORITY)==0){

ProcessRecord client=cr.binding.client;

……//当该进程的hiddenAdj大于客户端的hiddenAdj

/*如果此时客户端hiddenAdj>=VISIBLE_APP_ADJ,

*则该进程的hiddenAdj调整为客户端的hiddenAdj,

*否则,调整为VISIBLE_APP_ADJ。

*以该进程调整后的adj(myHiddenAdj)为基准,递归

调用computeOomAdjLocked重新计算客户端的adj/

clientAdj=computeOomAdjLocked(

client, myHiddenAdj, TOP_APP, true, doingAll);

String adjType=null;

……

/*如果在Client端绑定Service时设置了

BIND_ALLOW_OOM_MANAGEMENT绑定标记/

if(adj>clientAdj){

……

/*根据Client端新的adj值调整该进程的adj,有三种选择:

保留当前adj、调整为clientAdj、调整为VISIBLE_APP_ADJ/

}

if(adjType!=null){

……

//将该进程的“adj依赖源”设置为客户端

app.adjSource=cr.binding.client;

//设置“adj依赖源”的adj值

app.adjSourceOom=clientAdj;

//将Service设置为影响该进程adj值的目标

app.adjTarget=s.name;

}

……

}

//至此if(……BIND_WAIVE_PRIORITY)==0)分支结束

……

/*如果客户端绑定Service时,设置了

*BIND_ADJUST_WITH_ACTIVITY绑定标记,则调整该进程

的adj值调整为FOREGROUND_APP_ADJ或者保留当前值/

}//至此,for循环结束

}//至此,内层while循环结束

}//至此,外层if(s.connections.size()>0……)分支结束

}//至此,外层while循环结束

/*至此,计算出Service对adj的影响,如果计算结果大于hiddenAdj,需

要将其重新调整为hiddenAdj,防止adj值过大导致该进程被“杀死”/

if(adj>hiddenAdj){

adj=hiddenAdj;

app.hidden=false;

app.adjType="bg-services";

}

}


这部分代码繁琐而低效,其核心思路是尽可能将该进程的adj值与使用该进程Service的Client进程的adj值保持同步。

6.computeOomAdjLocked()第六阶段

computeOomAdjLocked()第六阶段主要工作是,根据使用该进程的Content Provider的Client的状态,调整进程的adj值。代码如下:


private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,

ProcessRecord TOP_APP, boolean recursed, boolean doingAll){

……//省略前五个阶段

//如果非前台进程,并且进程中包含发布的Content Provider

if(app.pubProviders.size()!=0&&(……)){

Iterator<ContentProviderRecord>jt=app.pubProviders.values().iterator();

while(jt.hasNext()&&(adj>ProcessList.FOREGROUND_APP_ADJ

||schedGroup==Process.THREAD_GROUP_BG_NONINTERACTIVE)){

ContentProviderRecord cpr=jt.next();

for(int i=cpr.connections.size()-1;……){

ContentProviderConnection conn=cpr.connections.get(i);

ProcessRecord client=conn.client;

……//如果客户端是该进程本身,则略过该客户端

/*当该进程的hiddenAdj大于客户端的hiddenAdj,

*如果此时客户端hiddenAdj>FOREGROUND_APP_ADJ,

/则该进程的hiddenAdj调整为客户端的hiddenAdj,/

*否则,调整为FOREGROUND_APP_ADJ。

*以该进程调整后的adj(myHiddenAdj)为基准,递归

调用computeOomAdjLocked重新计算客户端的adj/

int clientAdj=computeOomAdjLocked(

client, myHiddenAdj, TOP_APP, true, doingAll);

if(adj>clientAdj){

……/*根据Client新的adj值调整该进程的adj,有三种选择:

保留当前adj、调整为clientAdj、调整为FOREGROUND_APP_ADJ//

}

……

}

/*有运行于framework外部的进程使用该进程的Content Provider,这样

*的进程往往是native进程,独立于AMS的进程管理,不容易被“杀死”,因此

调整Content Provider所属进程的adj值为FOREGROUND_APP_ADJ/

if(cpr.hasExternalProcessHandles()){

if(adj>ProcessList.FOREGROUND_APP_ADJ){

adj=ProcessList.FOREGROUND_APP_ADJ;

schedGroup=Process.THREAD_GROUP_DEFAULT;

app.hidden=false;

app.keeping=true;

app.adjType="provider";

app.adjTarget=cpr.name;

}

}

}

}


这部分源码与第五阶段相似,但更简单,其核心思路是,尽可能将该进程的adj值与使用该进程Content Provider的Client进程的adj值保持同步。

7.computeOomAdjLocked()第七阶段

computeOomAdjLocked()第七阶段的主要工作如下:

将一部分进程的adj由SERVICE_ADJ调整为SERVICE_B_ADJ。

将“无正在Stopping的Activity”时进程的adj值赋值为当前计算结果。

调整“有正在Stopping的Activity”时进程的adj为PERCEPTIBLE_APP_ADJ。

调整进程的当前初始adj为当前计算结果。

将adj小于HIDDEN_APP_MIN_ADJ视为保留进程,不容易被“杀死”。

处理BIND_ABOVE_CLIENT标记时,对当前进程adj降级处理。

代码如下:


private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,

ProcessRecord TOP_APP, boolean recursed, boolean doingAll){

……//省略前六个阶段

if(adj==ProcessList.SERVICE_ADJ){

if(doingAll){//本例传入的参数为true

/*mNumServiceProcs是上次计算adj时应用程序Service的数量,

*mNewNumServiceProcs是本次计算adj时应用程序Service的数量,

*当mNewNumServiceProcs大于mNumServiceProcs的1/3时,将该

进程的adj调整为SERVICE_B_ADJ/

app.serviceb=mNewNumServiceProcs>(mNumServiceProcs/3);

mNewNumServiceProcs++;

}

if(app.serviceb){

adj=ProcessList.SERVICE_B_ADJ;

}

}else{

app.serviceb=false;

}

app.nonStoppingAdj=adj;//无正在Stopping的Activity时,进程的adj

//进程有正在Stopping的Activity时,调整其adj为PERCEPTIBLE_APP_ADJ

if(hasStoppingActivities){

if(adj>ProcessList.PERCEPTIBLE_APP_ADJ){

adj=ProcessList.PERCEPTIBLE_APP_ADJ;

app.adjType="stopping";

}

}

app.curRawAdj=adj;//设置进程的当前初始adj

//计算出的adj不能大于进程的maxAdj,否则调整为maxAdj

if(adj>app.maxAdj){//maxAdj创建ProcessRecord时指定

adj=app.maxAdj;

if(app.maxAdj<=ProcessList.PERCEPTIBLE_APP_ADJ){

schedGroup=Process.THREAD_GROUP_DEFAULT;

}

}

//adj小于HIDDEN_APP_MIN_ADJ视为保留进程,不容易被“杀死”

if(adj<ProcessList.HIDDEN_APP_MIN_ADJ){

app.keeping=true;

}

/*如果该进程通过BIND_ABOVE_CLIENT标记bindService到其他服务,说

*明该进程的地位比Service所在进程要低,需要将该进程的adj值适当

调高,当OOM发生时,尽可能让该进程先于Service所在进程被回收/

if(app.hasAboveClient){

if(adj<ProcessList.FOREGROUND_APP_ADJ){

}else if(adj<ProcessList.VISIBLE_APP_ADJ){

adj=ProcessList.VISIBLE_APP_ADJ;

}else if(adj<ProcessList.PERCEPTIBLE_APP_ADJ){

adj=ProcessList.PERCEPTIBLE_APP_ADJ;

}else if(adj<ProcessList.HIDDEN_APP_MIN_ADJ){

adj=ProcessList.HIDDEN_APP_MIN_ADJ;

}else if(adj<ProcessList.HIDDEN_APP_MAX_ADJ){

adj++;

}

}


8.computeOomAdjLocked()第八阶段

computeOomAdjLocked()第八阶段的主要工作如下:

根据adj值计算进程的importance值。

如果进程运行状态发生改变,需要将改变信息存入ActivityManagerService.mPendingProcessChanges成员变量中,然后向ActivityManagerService的消息循环中发送DISPATCH_PROCESSES_CHANGED消息,处理该消息时会回调IProcessObserver的3个方法:onForegroundActivitiesChanged、onImportanceChanged、onProcessDied。

IProcessObserver是一个Binder服务接口,目前还没有实现该接口的服务。代码如下:


private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,

ProcessRecord TOP_APP, boolean recursed, boolean doingAll){

……//省略前七个阶段

int importance=app.memImportance;

//由adj值计算进程的importance值

if(importance==0||adj!=app.curAdj||

schedGroup!=app.curSchedGroup){

app.curAdj=adj;

app.curSchedGroup=schedGroup;

if(!interesting){

//非interesting进程的importance值为IMPORTANCE_BACKGROUND

importance=ActivityManager.RunningAppProcessInfo.

IMPORTANCE_BACKGROUND;

}else if(adj>=ProcessList.HIDDEN_APP_MIN_ADJ){

importance=……//IMPORTANCE_BACKGROUND

}else if(adj>=ProcessList.SERVICE_B_ADJ){

importance=……//IMPORTANCE_SERVICE

}

……//省略其他分支

}

//以下条件改变时,视为进程运行状态发生变化

int changes=importance!=app.memImportance?

ProcessChangeItem.CHANGE_IMPORTANCE:0;

if(foregroundActivities!=app.foregroundActivities){

changes|=ProcessChangeItem.CHANGE_ACTIVITIES;

}

if(changes!=0){

app.memImportance=importance;

app.foregroundActivities=foregroundActivities;

int i=mPendingProcessChanges.size()-1;

ProcessChangeItem item=null;

……

/*如果进程运行状态发生变化,将变化值存入AMS的mPendingProcessChanges

成员变量中,并发送DISPATCH_PROCESSES_CHANGED消息/

}

return app.curRawAdj;

}


至此,computeOomAdjLocked执行完毕,返回updateOomAdjLocked方法中。接下来分析更新OOM adj的效果。