源码基于:Android R
在博文《oom_adj 内存水位算法剖析》一文中详细的分析了lmkd 中针对 oom_adj 内存水位的计算、使用方法,在博文《oom_adj 更新原理(1)》、《oom_adj 更新原理(2)》中对Android 系统中 oom_adj 的更新原理进行了详细的剖析。通过这几篇博文我们对 oom_adj 有了更深地了解。
本文在之前博文的基础上,剖析代码细节,对每个 oom_adj 的值进行详细地解读。
首先如之前几篇博文,还是先把 oom_adj 的值列举出来,下文将对每个 oom_adj 的值进行单独地解读。
frameworks/base/services/core/java/com/android/server/am/ProcessList.java
ADJ 等级 | 取值 | 说明 |
UNKNOWN_ADJ | 1001 | 用于特定地方,一般指将要cache进程,不知道确切值 |
CACHED_APP_MAX_ADJ | 999 | 不可见进程的最大值,进程可以无任何干扰的被杀 |
CACHED_APP_LMK_FIRST_ADJ | 950 | oom_adj 等级第一个允许被杀的level,不能等于CACHED_APP_MAX_ADJ |
CACHED_APP_MIN_ADJ | 900 | 不可见进程的最小值 |
SERVICE_B_ADJ | 800 | B List中的Service(较老的、使用可能性更小) |
PREVIOUS_APP_ADJ | 700 | 上一个App的进程(往往通过按返回键) |
HOME_APP_ADJ | 600 | Home进程 |
SERVICE_ADJ | 500 | app service 进程,杀掉它一般不会有太大的影响 |
HEAVY_WEIGHT_APP_ADJ | 400 | 后台的重量级进程,system/rootdir/init.rc文件中startup |
BACKUP_APP_ADJ | 300 | 备份进程,杀掉它不完全致命,但不好 |
PERCEPTIBLE_LOW_APP_ADJ | 250 | 被用户或系统绑定的进程,比service 要重要,但是如果被杀掉,不会立即影响客户的感官 |
PERCEPTIBLE_APP_ADJ | 200 | 可感知的进程,如后台music 播放 |
VISIBLE_APP_ADJ | 100 | 可视进程 |
PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ | 50 | recently TOP 进程 |
FOREGROUND_APP_ADJ | 0 | 前台进程 |
PERSISTENT_SERVICE_ADJ | -700 | 关联系统的进程或一个常驻进程 |
PERSISTENT_PROC_ADJ | -800 | 系统常驻进程,例如telephony 绝对不想杀,但不完全致命 |
SYSTEM_ADJ | -900 | 系统进程 |
NATIVE_ADJ | -1000 | native 进程,不归系统管理,所以没有oom_adj 适配 |
在 adj 的分级中 FOREGROUND_APP_ADJ 的值为0,拥有该 adj 的进程为前台进程。系统认为小于 0 的进程为系统中比较重要的进程。如下:
SYSTEM_ADJ 仅指 system_server 进程。在 SystemServer.startBootstrapServices() 过程中会调用 AMS.setSystemProcess(),而该函数中会将system_server 的maxAdj 设置为 SYSTEM_ADJ:
frameworks/base/services/core/java/com/android/server/am/AMS.java
public void setSystemProcess() {
try {
...
synchronized (this) {
ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
false,
0,
new HostingRecord("system"));
app.setPersistent(true);
app.pid = app.mPidForCompact = MY_PID;
app.getWindowProcessController().setPid(MY_PID);
app.maxAdj = ProcessList.SYSTEM_ADJ; //----这里设定system_server 的 maxAdj
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
addPidLocked(app);
mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
}
...
}
在调用 updateOomAdjLocked() 函数的时候传入的是一个参数,从博文《oom_adj 更新原理(2)》中的 computeOomAdjLocked() 得知:
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
...
app.curAdj = app.maxAdj;
...
}
最终的system_server 进程的 adj 被设置为 -900,从 dumpsys meminfo 中也能看到:
153,028K: System
153,028K: system (pid 1143 / adj -900)
PERSISTENT_PROC_ADJ 的进程需要在 AndroidManifest.xml 中声明 android:persistent 属性为 true 的系统 (带有 FLAG_SYSTEM) 进程,又称 persistent 进程。对于 persistent 进程常规情况下是不会被 kill 的,一旦被kill 或者发生 crash,系统也会立即重启拉起该进程。
设定位置:
在新的application 启动过程中都会调用 newProcessRecordLocked(),甚至包括 system_server 进程,例如 上一节 提到的 setSystemProcess() 函数:
frameworks/base/services/core/java/com/android/server/am/AMS.java
public void setSystemProcess() {
try {
...
synchronized (this) {
ProcessRecord app = mProcessList.newProcessRecordLocked(info, info.processName,
false,
0,
new HostingRecord("system"));
app.setPersistent(true);
app.pid = app.mPidForCompact = MY_PID;
app.getWindowProcessController().setPid(MY_PID);
app.maxAdj = ProcessList.SYSTEM_ADJ; //----这里设定system_server 的 maxAdj
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
addPidLocked(app);
mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
}
...
}
代码中第一句话就是通过 newProcessRecordLocked() 函数创建 ProcessRecord 对象。
来看下该函数:
frameworks/base/services/core/java/com/android/server/am/ProcessList.java
final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
boolean isolated, int isolatedUid, HostingRecord hostingRecord) {
String proc = customProcess != null ? customProcess : info.processName;
final int userId = UserHandle.getUserId(info.uid);
int uid = info.uid;
if (isolated) {
...
}
final ProcessRecord r = new ProcessRecord(mService, info, proc, uid);
if (!mService.mBooted && !mService.mBooting
&& userId == UserHandle.USER_SYSTEM
&& (info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
// The system process is initialized to SCHED_GROUP_DEFAULT in init.rc.
r.setCurrentSchedulingGroup(ProcessList.SCHED_GROUP_DEFAULT);
r.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
r.setPersistent(true);
r.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (isolated && isolatedUid != 0) {
// Special case for startIsolatedProcess (internal only) - assume the process
// is required by the system server to prevent it being killed.
r.maxAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
}
addProcessNameLocked(r);
return r;
}
通过函数得知,只有在 system_server 启动时会被置为 PERSISTENT_PROC_ADJ,但在 setSystemProcess() 最后会将system_server 的 maxAdj 修改为 SYSTEM_ADJ。
如果该进程是 isolated,maxAdj 会被设为 PERSISTENT_SERVICE_ADJ,详细可以查看第 2.3 节。
来看下另一个函数 addAppLocked():
frameworks/base/services/core/java/com/android/server/am/AMS.java
final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
boolean disableHiddenApiChecks, boolean disableTestApiChecks,
boolean mountExtStorageFull, String abiOverride, int zygotePolicyFlags) {
ProcessRecord app;
if (!isolated) {
app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
info.uid, true);
} else {
app = null;
}
if (app == null) {
app = mProcessList.newProcessRecordLocked(info, customProcess, isolated, 0,
new HostingRecord("added application",
customProcess != null ? customProcess : info.processName));
mProcessList.updateLruProcessLocked(app, false, null);
updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_PROCESS_BEGIN);
}
...
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.setPersistent(true);
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
mProcessList.startProcessLocked(app, new HostingRecord("added application",
customProcess != null ? customProcess : app.processName),
zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks,
mountExtStorageFull, abiOverride);
}
return app;
}
当 application 的 flag 置 FLAG_SYSTEM | FLAG_PERSISTENT 时,maxAdj 的值才被设为 PERSISENT_PROC_ADJ。
在SystemServer 启动 startOtherServices() 中会调用 AMS.systemReady() 进行boot 的 完成阶段,此处会调用 startPersistentApps():
frameworks/base/services/core/java/com/android/server/am/AMS.java
void startPersistentApps(int matchFlags) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;
synchronized (this) {
try {
final List apps = AppGlobals.getPackageManager()
.getPersistentApplications(STOCK_PM_FLAGS | matchFlags).getList();
for (ApplicationInfo app : apps) {
if (!"android".equals(app.packageName)) {
addAppLocked(app, null, false, null /* ABI override */,
ZYGOTE_POLICY_FLAG_BATCH_LAUNCH);
}
}
} catch (RemoteException ex) {
}
}
}
这里加载系统的 persistent 进程,调用 addAppLocked() 函数。
PERSISTENT_SERVICE_ADJ 的进程是 startIsolatedProcess() 方式启动的进程,或者是由 system_server 或 persistent 进程所绑定的服务进程。
我们在上一文 newProcessRecordLocked() 中已经提到过了,当进程为 isolated 时,maxAdj 会被设置为 PERSISTENT_SERVICE_ADJ,而newProcessRecordLocked() 触发的流程如下:
AMS.startIsolatedProcess() -> ProcessList.startProcessLocked() ->newProcessRecordLocked()
另外一种就是在 updateOomAdjLocked() 中会调用 computeOomAdjLocked():
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients) {
...
int capabilityFromFGS = 0; // capability from foreground service.
for (int is = app.numberOfRunningServices() - 1;
is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > PROCESS_STATE_TOP);
is--) {
ServiceRecord s = app.getRunningServiceAt(is);
...
ArrayMap> serviceConnections = s.getConnections();
for (int conni = serviceConnections.size() - 1;
conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > PROCESS_STATE_TOP);
conni--) {
ArrayList clist = serviceConnections.valueAt(conni);
for (int i = 0;
i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
|| schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
|| procState > PROCESS_STATE_TOP);
i++) {
...
int clientAdj = client.getCurRawAdj();
int clientProcState = client.getCurRawProcState();
if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
...
if (adj > clientAdj) {
if (app.hasShownUi && !app.getCachedIsHomeProcess()
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
if (adj >= ProcessList.CACHED_APP_MIN_ADJ) {
adjType = "cch-bound-ui-services";
}
} else {
int newAdj;
if ((cr.flags&(Context.BIND_ABOVE_CLIENT
|Context.BIND_IMPORTANT)) != 0) {
if (clientAdj >= ProcessList.PERSISTENT_SERVICE_ADJ) {
newAdj = clientAdj;
} else {
newAdj = ProcessList.PERSISTENT_SERVICE_ADJ;
...
}
}
当前进程绑定有client,且当前进程 adj 优先级还没有 client 优先级高时,确定是否设置了 BIND_ABOVE_CLIENT 或 BIND_IMPORTANT 的flags
场景1:满足以下任一条件的进程都属于FOREGROUND_APP_ADJ(0) 优先级:
详细代码可以查看博文《oom_adj 更新原理(2)》中的 computeOomAdjLocked() :
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients) {
...
boolean foregroundActivities = false;
if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) {
// The last app on the list is the foreground app.
adj = ProcessList.FOREGROUND_APP_ADJ;
...
} else if (app.runningRemoteAnimation) {
adj = ProcessList.VISIBLE_APP_ADJ;
...
} else if (app.getActiveInstrumentation() != null) {
adj = ProcessList.FOREGROUND_APP_ADJ;
...
} else if (app.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) {
adj = ProcessList.FOREGROUND_APP_ADJ;
...
} else if (app.executingServices.size() > 0) {
adj = ProcessList.FOREGROUND_APP_ADJ;
...
} else if (app == topApp) {
adj = ProcessList.FOREGROUND_APP_ADJ;
...
} else {
procState = PROCESS_STATE_CACHED_EMPTY;
...
}
场景2: 当客户端进程activity里面调用bindService()方法时flags带有BIND_ADJUST_WITH_ACTIVITY参数,并且该activity处于可见状态,则当前服务进程也属于前台进程,源码如下:
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients) {
...
final ActivityServiceConnectionsHolder a = cr.activity;
if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
&& a.isActivityVisible()) {
adj = ProcessList.FOREGROUND_APP_ADJ;
app.setCurRawAdj(adj);
场景3: 对于provider进程,还有以下两个条件能成为前台进程:
可见进程:当ActivityRecord的visible=true,也就是Activity可见的进程。
从Android P开始,进一步细化ADJ级别,增加了 VISIBLE_APP_LAYER_MAX(99),是指VISIBLE_APP_ADJ(100) 跟 PERCEPTIBLE_APP_ADJ(200) 之间有99个槽,则可见级别ADJ的取值范围为[100,199]。 算法会根据其所在task的mLayerRank来调整其ADJ,100加上mLayerRank就等于目标ADJ,layer越大,则ADJ越小。
关于TaskRecord的mLayerRank的计算方式是在updateOomAdjLocked()过程调用ASS的rankTaskLayersIfNeeded() 方法。
当TaskRecord顶部的ActivityRecord为空或者结束或者不可见时,则设置该TaskRecord的mLayerRank等于-1; 每个ActivityDisplay的baseLayer都是从0开始,从最上面的TaskRecord开始,第一个ADJ=100,从上至下依次加1,直到199为上限。
可感知进程:当该进程存在不可见的Activity,但Activity正处于PAUSING、PAUSED、STOPPING状态,则为PERCEPTIBLE_APP_ADJ。
在 computeOomAdjLocked() 函数中对于可感知进程也有专门的计算:
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients) {
...
/********* 场景 1 **********/
// Examine all activities if not already foreground.
if (!foregroundActivities && app.getCachedHasActivities()) {
app.computeOomAdjFromActivitiesIfNecessary(mTmpComputeOomAdjWindowCallback,
adj, foregroundActivities, procState, schedGroup, appUid, logUid,
PROCESS_STATE_CUR_TOP);
adj = app.mCachedAdj;
...
}
...
/********* 场景 2 **********/
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
|| procState > PROCESS_STATE_FOREGROUND_SERVICE) {
if (app.hasForegroundServices()) {
// The user is aware of this app, so make it visible.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
procState = PROCESS_STATE_FOREGROUND_SERVICE;
app.adjType = "fg-service";
app.setCached(false);
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
} else if (app.hasOverlayUi()) {
// The process is display an overlay UI.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
procState = PROCESS_STATE_IMPORTANT_FOREGROUND;
app.setCached(false);
app.adjType = "has-overlay-ui";
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
...
/********* 场景 3 **********/
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
|| procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
if (app.forcingToImportant != null) {
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
app.setCached(false);
app.adjType = "force-imp";
app.adjSource = app.forcingToImportant;
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
}
}
场景1:如果不是前台进程,且存在cache activities,则需要继续确定adj
调用 computeOomAdjFromActivitiesIfNecessary(),这里是在计算 oom_adj 时,传入一个 callback,通过 window 来确认activity 处于什么状态 visible 还是PAUSING、STOPPING 或其他状态,并根据状态调用 callback 对应的回调函数。
场景2:进程拥有前台services 或进程用有 overlay UI
前提调价是进程重要性已经低于可感知级别,或者进程状态级别高于带前台service
拥有前台services,是执行 startForegroundService() 函数了;
hasOverlayUi() 为true,表示非activity 的UI 位于屏幕最顶层,例如显示类型 TYPE_APPLICATION_OVERLAY 的窗口。
场景3:进程forcingToImportant 非空
该值非空,表示执行了 setProcessImportant() 函数,例如 Toast 弹出过程;
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients) {
...
final BackupRecord backupTarget = mService.mBackupTargets.get(app.userId);
if (backupTarget != null && app == backupTarget.app) {
// If possible we want to avoid killing apps while they're being backed up
if (adj > ProcessList.BACKUP_APP_ADJ) {
if (DEBUG_BACKUP) Slog.v(TAG_BACKUP, "oom BACKUP_APP_ADJ for " + app);
adj = ProcessList.BACKUP_APP_ADJ;
if (procState > PROCESS_STATE_TRANSIENT_BACKGROUND) {
procState = PROCESS_STATE_TRANSIENT_BACKGROUND;
}
app.adjType = "backup";
app.setCached(false);
}
if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
procState = ActivityManager.PROCESS_STATE_BACKUP;
app.adjType = "backup";
}
}
前提条件是 backupTarget 不为空:
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients) {
...
//重量级后台进程,把adj和proc_state都拉回到 heavy weight水平
if (app.getCachedIsHeavyWeight()) {
if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.setCached(false);
app.adjType = "heavy";
}
if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
app.adjType = "heavy";
}
}
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients) {
...
//HOME 进程,将其拉回HOME_APP_ADJ
if (app.getCachedIsHomeProcess()) {
if (adj > ProcessList.HOME_APP_ADJ) {
// This process is hosting what we currently consider to be the
// home app, so we don't want to let it go into the background.
adj = ProcessList.HOME_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.setCached(false);
app.adjType = "home";
}
if (procState > ActivityManager.PROCESS_STATE_HOME) {
procState = ActivityManager.PROCESS_STATE_HOME;
app.adjType = "home";
}
}
当类型为ACTIVITY_TYPE_HOME的应用启动后会设置mHomeProcess,比如桌面APP。
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java
private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj,
ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval,
boolean computeClients) {
...
/********* 场景 1 **********/
if (app.getCachedIsPreviousProcess() && app.getCachedHasActivities()) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {
// This was the previous process that showed UI to the user.
// We want to try to keep it around more aggressively, to give
// a good experience around switching between two apps.
adj = ProcessList.PREVIOUS_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.setCached(false);
app.adjType = "previous";
}
if (procState > PROCESS_STATE_LAST_ACTIVITY) {
procState = PROCESS_STATE_LAST_ACTIVITY;
app.adjType = "previous";
}
}
...
/********* 场景 2 **********/
if (app.lastProviderTime > 0 &&
(app.lastProviderTime + mConstants.CONTENT_PROVIDER_RETAIN_TIME) > now) {
if (adj > ProcessList.PREVIOUS_APP_ADJ) {
adj = ProcessList.PREVIOUS_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.setCached(false);
app.adjType = "recent-provider";
}
if (procState > PROCESS_STATE_LAST_ACTIVITY) {
procState = PROCESS_STATE_LAST_ACTIVITY;
app.adjType = "recent-provider";
}
}
在 computeOomAdjLocked() 函数用有两种场景。
场景1:用户上一个使用的包含UI的进程,为了给用户在两个APP之间更好的切换体验,将上一个进程ADJ设置到PREVIOUS_APP_ADJ的档次。 当activityStoppedLocked()过程会更新上一个应用。
场景2: 当 provider进程,上一次使用时间不超过20S的情况下,优先级不低于PREVIOUS_APP_ADJ。provider进程这个是Android 7.0以后新增的逻辑 ,这样做的好处是在内存比较低的情况下避免拥有provider的进程出现颠簸,也就是启动后杀,然后又被拉。