Anroid的杀进程策略是基于kernel里的LowMemoryKiller模块,LMK的实现在这里不展开分析,大致的原理就是LMK注册了内核的shrinker(lowmem_shrinker),内核线程kswapd,在linux回收内存分页的时候,通过shrinker回调回来给LMK。LMK根据每个进程的oom_adj值,将大于某个阈值的进程都发送SIGKILL信号杀掉。oom_adj的阈值因内存情况不同而不同,具体的对应关系可以查看/sys/module/lowmemorykiller/parameters/adj和/sys/module/lowmemorykiller/parameters/minfree这两个文件。oom_adj的值从-17到16,值越小,代表越重要,越晚被杀,比如一个应用如果在前台的时候,oom_adj的值就会减到0。也就是说设为负值的那些应用,会比前台应用还晚被杀。于是,系统杀进程的策略就可以通过调整每个进程的oom_adj的值来实现。
oom_adj的值有如下类:
// 不可见的Activity static final int CACHED_APP_MAX_ADJ = 15; static final int CACHED_APP_MIN_ADJ = 9; // 比较老的Service static final int SERVICE_B_ADJ = 8; // 上一个应用,这样在做任务切换,或者返回的时候,能够快速载入 static final int PREVIOUS_APP_ADJ = 7; // 桌面 App static final int HOME_APP_ADJ = 6; // Service static final int SERVICE_ADJ = 5; // 重量级应用,早期版本可以在manifest里加cantSaveState来声明,新版已经注释了,目前没看到哪里可以设置 static final int HEAVY_WEIGHT_APP_ADJ = 4; // 备份代理应用,manifest里Application标签里可以声明backAgent static final int BACKUP_APP_ADJ = 3; // 可感知的App,比如有Pause状态的Activity,Stopping状态的Activity,被一个可见的进程BindService的进程等 static final int PERCEPTIBLE_APP_ADJ = 2; // 前台可见的Activity, static final int VISIBLE_APP_ADJ = 1; // 前台App,包括Top App,Instrumentation Test App,正在接收broadcast的App,正在执行的Service等 static final int FOREGROUND_APP_ADJ = 0; // 被Persist App BindService的进程 static final int PERSISTENT_SERVICE_ADJ = -11; // 声明了persist的进程 static final int PERSISTENT_PROC_ADJ = -12; // 系统进程,比如system server static final int SYSTEM_ADJ = -16; // 不被系统管的Native进程,比如/system/bin下运行的那些服务(surfaceflinger etc) static final int NATIVE_ADJ = -17;
oom_adj的值受很多因素影响:应用是否有activity,activity是否可见,是否有service,service是否被bind的其他进程的oom_adj等等。在Framework里oom_adj的调整主要由ActivityManagerService这个类负责,任何可能会影响到进程oom_adj的值的情况,就会调用updateOomAdjLocked来更新各进程的oom_adj值,比如:
1,Activity切换
2,Service start/stop/bind
3,Broadcast分发处理
updateOomAdjLocked会遍历当前进程列表,对每个进程ProcessRecord都调用computeOomAdjLocked来重新计算oom_adj,最后applyOomAdjLocked来使oom_adj生效。
我们看看updateOomAdjLocked的实现:
final void updateOomAdjLocked() { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; final long now = SystemClock.uptimeMillis(); final long oldTime = now - ProcessList.MAX_EMPTY_TIME; final int N = mLruProcesses.size(); ...... ...... for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); // 找到最早的一次service活动时间 if (mEnableBServicePropagation && app.serviceb && (app.curAdj == ProcessList.SERVICE_B_ADJ)) { numBServices++; for (int s = app.services.size() - 1; s >= 0; s--) { ServiceRecord sr = app.services.valueAt(s); if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + app.processName + " serviceb = " + app.serviceb + " s = " + s + " sr.lastActivity = " + sr.lastActivity + " packageName = " + sr.packageName + " processName = " + sr.processName); if (SystemClock.uptimeMillis() - sr.lastActivity < mMinBServiceAgingTime) { if (DEBUG_OOM_ADJ) { Slog.d(TAG,"Not aged enough!!!"); } continue; } if (serviceLastActivity == 0) { serviceLastActivity = sr.lastActivity; selectedAppRecord = app; } else if (sr.lastActivity < serviceLastActivity) { serviceLastActivity = sr.lastActivity; selectedAppRecord = app; } } } if (DEBUG_OOM_ADJ && selectedAppRecord != null) Slog.d(TAG, "Identified app.processName = " + selectedAppRecord.processName + " app.pid = " + selectedAppRecord.pid); if (!app.killedByAm && app.thread != null) { app.procStateChanged = false; // 重新计算进程app的oom_adj computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); // If we haven't yet assigned the final cached adj // to the process, do that now. // 如果没找到对应的oom_adj,那么根据app的进程状态,如果有activity存在,那么oom_adj设为curCachedAdj, // 否则就是empty进程,讲oom_adj设为curEmptyAdj if (app.curAdj >= ProcessList.UNKNOWN_ADJ) { switch (app.curProcState) { case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: // This process is a cached process holding activities... // assign it the next cached value for that type, and then // step that cached level. app.curRawAdj = curCachedAdj; app.curAdj = app.modifyRawOomAdj(curCachedAdj); if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj + ")"); if (curCachedAdj != nextCachedAdj) { stepCached++; if (stepCached >= cachedFactor) { stepCached = 0; curCachedAdj = nextCachedAdj; nextCachedAdj += 2; if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) { nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; } } }