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的效果。