前言
前面一篇文章介紹了關(guān)于WMS在整個Android體系中的作用,主要可以劃分為四類職責(zé): 1.窗口管理 2.窗口動畫 3.Surface管理 4.輸入事件中轉(zhuǎn)站。
如果把WMS比作古代將軍,那么這四類職責(zé)就是將軍手下幾元大將,而AMS作為Android整個體系的統(tǒng)籌者,理所當(dāng)然的就是古代的皇帝。
而今天要講的是Android體系中比較重要的一個概念:AMS進(jìn)程管理
傳統(tǒng)的進(jìn)程是指程序執(zhí)行的載體,進(jìn)程退出也就意味著程序退出了,而在Android中,進(jìn)程的概念被弱化了,進(jìn)程成為一個運行組件的容器。如應(yīng)用中Service,即可以在宿主進(jìn)程中運行也可以在服務(wù)進(jìn)程中運行,服務(wù)進(jìn)程退出,只是某個Service的退出,并非應(yīng)用退出。
在Android中,谷歌將進(jìn)程的管理和調(diào)度封裝在了AMS中,應(yīng)用層無需關(guān)心進(jìn)程是如何工作的。
AMS對進(jìn)程的管理主要體現(xiàn)在兩個方面:
- 1.進(jìn)程LRU列表動態(tài)更新:動態(tài)調(diào)整進(jìn)程在mLruProcesses列表的位置
- 2.進(jìn)程優(yōu)先級動態(tài)調(diào)整:實際是調(diào)整進(jìn)程oom_adj的值。
這兩項調(diào)整和系統(tǒng)進(jìn)行自動回收有關(guān),當(dāng)內(nèi)存不足時,系統(tǒng)會關(guān)閉一些進(jìn)程來釋放內(nèi)存、
下面筆者就依據(jù)這兩方面來看下AMS是如何管理進(jìn)程的。
目錄

進(jìn)程LRU列表動態(tài)更新
如果你進(jìn)程看Android源碼,應(yīng)該會常??纯聪旅孢@個方法:updateLruProcessLocked。當(dāng)時可能只是了解有這么個方法做了個緩存進(jìn)程的事,但是具體是如何實現(xiàn)的并不知曉,總感覺看代碼少了點什么,下面我們會圍繞這個方法展開。
AMS中的updateLruProcessLocked實現(xiàn)了對進(jìn)程LRU列表動態(tài)更新: 在講解updateLruProcessLocked方法前,我們先來講解下mLruProcesses進(jìn)程列表在AMS中的模型。
LRU進(jìn)程列表數(shù)據(jù)結(jié)構(gòu)
AMS進(jìn)程的LRU列表mLruProcesses:
final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
AMS啟動的每個進(jìn)程都會被添加到LRU列表中,這個LRU列表不是隨意排序的或者僅僅根據(jù)先后順序排序的,而是根據(jù)具體規(guī)則進(jìn)行計算,以及進(jìn)程的當(dāng)前狀態(tài)進(jìn)行改變的、 LRU列表中存儲的是一個個ProcessRecord,AMS中使用ProcessRecord來代表一個進(jìn)程、內(nèi)部存儲了一個進(jìn)程所有的信息。
LRU列表被分為3段:
- 1.hasActivity:帶Activity的進(jìn)程
- 2.hasService:帶Service的進(jìn)程
- 3.other:其他進(jìn)程。
這三段使用兩個字段分割開:mLruProcessServiceStart和mLruProcessActivityStart,分別表示hasActivity段的開始位置以及hasService段的開始位置。
大概模型如下:

每次優(yōu)先級較高的進(jìn)程,如帶前臺Activity的進(jìn)程就會優(yōu)先被放到尾部,所以進(jìn)程優(yōu)先級由頭到尾
有了上面這個模型基礎(chǔ),下面我們從源碼角度來看LRU列表就更輕松了。
關(guān)鍵方法詳解
AMS使用updateLruProcessLocked方法對進(jìn)程列表進(jìn)行更新操作。
updateLruProcessLocked()方法在ActivityStack類中有3處可能被調(diào)用。
其中2處調(diào)用位置都處于ActivityStack類中的resumeTopActivityInnerLocked()方法:
1.pausing:通過home鍵返回或者back鍵退出一個Activity,此時進(jìn)程中不止一個Activity、
-
2.resume:熱啟動Activity
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { //省略。。 if (pausing && !resumeWhilePausing) { if (next.app != null && next.app.thread != null) { mService.updateLruProcessLocked(next.app, true, null); } } //省略 if (next.app != null && next.app.thread != null) { mService.updateLruProcessLocked(next.app, true, null); next.app.thread.scheduleResumeActivity(next.appToken....); } //省略 }
1處位于destroyActivityLocked()方法:如按back鍵退出最后一個Activity的時候。
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
if (hadApp) {
if (r.app.activities.isEmpty()) {
mService.updateLruProcessLocked(r.app, false, null);
mService.updateOomAdjLocked();
}
}
r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,..;
}
下面具體來看下該方法:
final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
ProcessRecord client) {
//1.判斷該進(jìn)程是否存在Activity
final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
|| app.treatLikeActivity;
//2.判斷進(jìn)程是否存在Service
final boolean hasService = false; // not impl yet. app.services.size() > 0;
//3.給LRU的序列號+1
mLruSeq++;
//4.如果hasActivity為true
if (hasActivity) {
final int N = mLruProcesses.size();
//如果當(dāng)前進(jìn)程有Activity且mLruProcesses最尾部的元素是當(dāng)前進(jìn)程,則什么都不用處理,直接退出
if (N > 0 && mLruProcesses.get(N-1) == app) {
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top activity: " + app);
return;
}
} else {
//如果當(dāng)前進(jìn)程沒有Activity且在Other段的top元素是當(dāng)前進(jìn)程,則也不處理,直接退出。
if (mLruProcessServiceStart > 0
&& mLruProcesses.get(mLruProcessServiceStart-1) == app) {
if (DEBUG_LRU) Slog.d(TAG_LRU, "Not moving, already top other: " + app);
return;
}
}
//5.獲取當(dāng)前進(jìn)程在mLruProcesses中的索引
int lrui = mLruProcesses.lastIndexOf(app);
//6.如果是persistent永久進(jìn)程,且索引不為0,則直接退出不處理
if (app.persistent && lrui >= 0) {
return;
}
//7.索引大于等于0的情況下,對mLruProcessActivityStart和mLruProcessServiceStart進(jìn)行更改并刪除列表對應(yīng)的索引上的進(jìn)程
if (lrui >= 0) {
if (lrui < mLruProcessActivityStart) {
mLruProcessActivityStart--;
}
if (lrui < mLruProcessServiceStart) {
mLruProcessServiceStart--;
}
mLruProcesses.remove(lrui);
}
int nextIndex;
if (hasActivity) {
final int N = mLruProcesses.size();
//8.如果hasActivity為true但是app.activities.size為0,其實就是1處的第二種判斷app.hasClientActivities為true,且mLruProcessActivityStart分割點沒超過列表進(jìn)程數(shù)
if (app.activities.size() == 0 && mLruProcessActivityStart < (N - 1)) {
//9.將進(jìn)程添加到mLruProcesses列表的倒數(shù)第二個位置,因為倒數(shù)第一個位置是提供給有Activity的進(jìn)程使用。切記帶索引的add方法只是插入不會覆蓋,被頂替的元素自動后移
mLruProcesses.add(N - 1, app);
final int uid = app.info.uid;
//10.為了防止當(dāng)前進(jìn)程創(chuàng)建很多Client端的進(jìn)程,導(dǎo)致進(jìn)程被濫用,將當(dāng)前進(jìn)程的子進(jìn)程Client往重要性低處的列表排序,直到碰到不是當(dāng)前進(jìn)程的子進(jìn)程Client端為止。
for (int i = N - 2; i > mLruProcessActivityStart; i--) {
ProcessRecord subProc = mLruProcesses.get(i);
if (subProc.info.uid == uid) {
if (mLruProcesses.get(i - 1).info.uid != uid) {
//交換i和i-1位置的進(jìn)程元素
ProcessRecord tmp = mLruProcesses.get(i);
mLruProcesses.set(i, mLruProcesses.get(i - 1));
mLruProcesses.set(i - 1, tmp);
i--;
}
} else {
// A gap, we can stop here.
//如果出現(xiàn)一個uid不一致的退出for循環(huán)交換
break;
}
}
} else {
//11.對于有Activity的進(jìn)程,則直接將進(jìn)程添加到末尾。
mLruProcesses.add(app);
}
//設(shè)置nextIndex為mLruProcessServiceStart
nextIndex = mLruProcessServiceStart;
} else if (hasService) {
//12.如果是有Service的進(jìn)程,則將進(jìn)程插入到hasService段的末尾,也就是hasActivity段的開頭位置
mLruProcesses.add(mLruProcessActivityStart, app);
//設(shè)置nextIndex為mLruProcessServiceStart
nextIndex = mLruProcessServiceStart;
//將mLruProcessActivityStart hasActivity的起始索引+1;
mLruProcessActivityStart++;
} else {
// Process not otherwise of interest, it goes to the top of the non-service area.
int index = mLruProcessServiceStart;
//方法的第三個參數(shù)client一般都為null,這里不進(jìn)入
if (client != null) {
//省略。。
}
//13.對于其他也沒Activity也沒Service的情況,則將進(jìn)程對象下添加到Other字段末尾:此時index = mLruProcessServiceStart,也就是Other字段的末尾。
mLruProcesses.add(index, app);
//插入的索引的前一個索引位置
nextIndex = index-1;
//mLruProcessActivityStart和mLruProcessServiceStart索引均向后移動1位。
mLruProcessActivityStart++;
mLruProcessServiceStart++;
}
//對于有Service和ContentProvider的情況,也需要將Service的進(jìn)程和ContentProvider的進(jìn)程對象也插入到列表中。
for (int j=app.connections.size()-1; j>=0; j--) {
ConnectionRecord cr = app.connections.valueAt(j);
if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
&& cr.binding.service.app != null
&& cr.binding.service.app.lruSeq != mLruSeq
&& !cr.binding.service.app.persistent) {
nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
"service connection", cr, app);
}
}
for (int j=app.conProviders.size()-1; j>=0; j--) {
ContentProviderRecord cpr = app.conProviders.get(j).provider;
if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
"provider reference", cpr, app);
}
}
}
方法每個步驟已經(jīng)在代碼中做了說明,如果你仔細(xì)對照前面說的模型去看,一定能看懂。 這里額外說明下兩點:
- 1.對于永久性的進(jìn)程即設(shè)置了persistent標(biāo)志的進(jìn)程在列表中的位置不會更改。
- 2.mLruProcessActivityStart和mLruProcessServiceStart會隨著列表的改變而改變,而不是固定的。
- 3.為了防止某些進(jìn)程自己又沒Activity,卻可能創(chuàng)建很多Client端的進(jìn)程,導(dǎo)致進(jìn)程被濫用的情況。會將當(dāng)前進(jìn)程的子進(jìn)程Client往重要性低處的列表排序,直到碰到不是當(dāng)前進(jìn)程的子進(jìn)程Client端為止。
- 4.對于有Service和ContentProvider的情況,也需要將Service的進(jìn)程和ContentProvider的進(jìn)程對象也插入到LRU列表中。
看圖說話:

好了,關(guān)于進(jìn)程列表的動態(tài)更新就講到這里。下面我們來講解進(jìn)程優(yōu)先級動態(tài)調(diào)整。
進(jìn)程優(yōu)先級動態(tài)調(diào)整
AMS中的updateOomAdjLocked方法實現(xiàn)了進(jìn)程優(yōu)先級的動態(tài)更新。 在講解updateOomAdjLocked方法前,我們先來了解下與進(jìn)程相關(guān)的幾個重要概念。
進(jìn)程優(yōu)先級(OOM_ADJ)
OOM_ADJ定義在ProcessList.java文件,大概劃分為20個級。
| ADJ級別 | adjString | 取值 | 解釋 |
|---|---|---|---|
| UNKNOWN_ADJ | 1001 | 預(yù)留的最低級別,一般對于緩存的進(jìn)程才有可能設(shè)置成這個級別 | |
| CACHED_APP_MAX_ADJ | 999 | 不可見進(jìn)程的adj最大值,在內(nèi)存不足的情況下就會優(yōu)先被kill。 | |
| CACHED_APP_LMK_FIRST_ADJ | 950 | lowmem 查殺的最小等級 | |
| CACHED_APP_MIN_ADJ | cch | 900 | 不可見進(jìn)程的adj最小值,在內(nèi)存不足的情況下就會優(yōu)先被kill |
| SERVICE_B_ADJ | svcb | 800 | 非活躍進(jìn)程,B List中的Service(運行時間較長、使用可能性更小) |
| PREVIOUS_APP_ADJ | prev | 700 | 上一個App的進(jìn)程(上一個stopActivity的進(jìn)程/20s內(nèi)剛被使用的provider進(jìn)程) |
| HOME_APP_ADJ | home | 600 | Home進(jìn)程 |
| SERVICE_ADJ | svc | 500 | 服務(wù)進(jìn)程(Service process) |
| HEAVY_WEIGHT_APP_ADJ | hvy | 400 | 后臺的重量級進(jìn)程 |
| BACKUP_APP_ADJ | bkup | 300 | 備份進(jìn)程 |
| PERCEPTIBLE_LOW_APP_ADJ | prcl | 250 | 由系統(tǒng)(或其他應(yīng)用程序)綁定的進(jìn)程,它比服務(wù)更重要,但不易察覺(clientAdj<200通過BIND_NOT_PERCEPTIBLE bind) |
| PERCEPTIBLE_APP_ADJ | prcp | 200 | 可感知進(jìn)程,比如后臺音樂播放 (前臺服務(wù)/display an overlay UI/currently used for toasts/clientAdj<200通過BIND_NOT_VISIBLE bind) |
| VISIBLE_APP_ADJ(VISIBLE_APP_LAYER_MAX200-100-1) | vis | 100 | 可見進(jìn)程(Visible process) ,一般是100+當(dāng)前可見的layer數(shù):activity不在前臺,但是確實可見的或者正在運行遠(yuǎn)程動畫 |
| PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ | 50 | 應(yīng)用有前臺服務(wù),從前臺切換到前臺service,且在15s內(nèi)到過前臺 | |
| FOREGROUND_APP_ADJ | fg | 0 | 前臺進(jìn)程(Foreground process):應(yīng)用本身就是在前臺或者正在接收處理廣播isReceivingBroadcastLocked或者服務(wù)執(zhí)行過程中 |
| PERSISTENT_SERVICE_ADJ | psvc | -700 | 關(guān)聯(lián)著系統(tǒng)或persistent進(jìn)程(由startIsolatedProcess()方式啟動的進(jìn)程,或者是由system_server或者persistent進(jìn)程所綁定的服務(wù)進(jìn)程) |
| PERSISTENT_PROC_ADJ | pers | -800 | 系統(tǒng)persistent進(jìn)程,比如telephony(一般不會被殺,即使被殺或crash,立即重啟) |
| SYSTEM_ADJ | sys | -900 | 系統(tǒng)進(jìn)程(system_server進(jìn)程) |
| NATIVE_ADJ | ntv | -1000 | native進(jìn)程(由init進(jìn)程fork出的進(jìn)程,并不受system管控) |
獲取oom_adj:
adb shell ps|grep com.android.yuhb.test
adb shell cat /proc/21375/oom_adj
每個等級的進(jìn)程又有對應(yīng)的優(yōu)先級,使用oom_adj值來表示,進(jìn)程回收機(jī)制就是根據(jù)這個adj值來進(jìn)行的 前臺進(jìn)程adj值最低,代表進(jìn)程優(yōu)先級最高,空進(jìn)程adj值越高,最容易被kill,對于相等優(yōu)先級的進(jìn)程:使用的內(nèi)存越多越容易被殺死
進(jìn)程state級別(ProcState)
ProcState定義在ActivityManager.java文件,大概劃分為22類。用來表示當(dāng)前進(jìn)程的一組狀態(tài)
| state級別 | procStateString | 取值 | 解釋 |
|---|---|---|---|
| PROCESS_STATE_NONEXISTENT | NONE | 20 | 不存在的進(jìn)程 |
| PROCESS_STATE_CACHED_EMPTY | CEM | 19 | 處于cached狀態(tài)的空進(jìn)程 |
| PROCESS_STATE_CACHED_RECENT | CRE | 18 | 有activity在最近任務(wù)列表的cached進(jìn)程 |
| PROCESS_STATE_CACHED_ACTIVITY_CLIENT | CACC | 17 | 進(jìn)程處于cached狀態(tài),且為另一個cached進(jìn)程(內(nèi)含Activity)的client進(jìn)程 |
| PROCESS_STATE_CACHED_ACTIVITY | CAC | 16 | 進(jìn)程處于cached狀態(tài)(內(nèi)含Activity) |
| PROCESS_STATE_LAST_ACTIVITY | LAST | 15 | 后臺進(jìn)程(擁有上一次顯示的Activity) |
| PROCESS_STATE_HOME | HOME | 14 | 后臺進(jìn)程(擁有home Activity) |
| PROCESS_STATE_HEAVY_WEIGHT | HVY | 13 | 后臺進(jìn)程(但無法執(zhí)行restore,因此盡量避免kill該進(jìn)程) |
| PROCESS_STATE_TOP_SLEEPING | TPSL | 12 | 與PROCESS_STATE_TOP一樣,但此時設(shè)備正處于休眠狀態(tài) |
| PROCESS_STATE_RECEIVER | RCVR | 11 | 后臺進(jìn)程,且正在運行receiver |
| PROCESS_STATE_SERVICE | SVC | 10 | 后臺進(jìn)程,且正在運行service |
| PROCESS_STATE_BACKUP | BKUP | 9 | 后臺進(jìn)程,正在運行backup/restore操作 |
| PROCESS_STATE_TRANSIENT_BACKGROUND | TRNB | 8 | 后臺進(jìn)程 |
| PROCESS_STATE_IMPORTANT_BACKGROUND | IMPB | 7 | 對用戶很重要的進(jìn)程,用戶不可感知其存在 |
| PROCESS_STATE_IMPORTANT_FOREGROUND | IMPF | 6 | 對用戶很重要的進(jìn)程,用戶可感知其存在 |
| PROCESS_STATE_BOUND_FOREGROUND_SERVICE , | BFGS | 5 | 通過系統(tǒng)綁定擁有一個前臺Service |
| PROCESS_STATE_FOREGROUND_SERVICE | FGS | 4 | 擁有一個前臺Service |
| PROCESS_STATE_BOUND_TOP | BTOP | 3 | 綁定到top應(yīng)用的進(jìn)程 |
| PROCESS_STATE_TOP | TOP | 2 | 擁有當(dāng)前用戶可見的top Activity |
| PROCESS_STATE_PERSISTENT_UI | PERU | 1 | persistent系統(tǒng)進(jìn)程,并正在執(zhí)行UI操作 |
| PROCESS_STATE_PERSISTENT | PER | 0 | persistent系統(tǒng)進(jìn)程 |
| PROCESS_STATE_UNKNOWN | -1 | UNKNOWN進(jìn) |
進(jìn)程組schedGroup
用來表示當(dāng)前進(jìn)程所在的進(jìn)程調(diào)度組序列。
| schedGroup | 值 | 含義 |
|---|---|---|
| SCHED_GROUP_BACKGROUN | 0 | 后臺進(jìn)程組 |
| SCHED_GROUP_RESTRICTED | 1 | |
| SCHED_GROUP_DEFAULT | 2 | 前臺進(jìn)程組 |
| SCHED_GROUP_TOP_APP | 3 | TOP進(jìn)程組 |
| SCHED_GROUP_TOP_APP_BOUND | 4 | TOP進(jìn)程組 |
LMK機(jī)制
LMK 全稱 Low Memory Killer`。
在Android中,即使當(dāng)用戶退出應(yīng)用程序后,應(yīng)用進(jìn)程也還會存在內(nèi)存中,方便下次可以快速進(jìn)入應(yīng)用而不需要重新創(chuàng)建進(jìn)程。 這樣帶來的直接影響就是由于進(jìn)程數(shù)量越來越多,系統(tǒng)內(nèi)存會越來越少,這個時候就需要殺死一部分進(jìn)程來緩解內(nèi)存壓力。 至于哪些進(jìn)程會被殺死,這個時候就需要用到Low Memory Killer機(jī)制來進(jìn)行判定。
Android的Low Memory Killer基于Linux的OOM機(jī)制, 在Linux中,內(nèi)存是以頁面為單位分配的,當(dāng)申請頁面分配時如果內(nèi)存不足會通過以下流程選擇bad進(jìn)程來殺掉從而釋放內(nèi)存
alloc_pages -> out_of_memory() -> select_bad_process() -> badness()
LMK驅(qū)動層在用戶空間指定了一組內(nèi)存臨界值及與之一一對應(yīng)的一組oom_adj值, 當(dāng)系統(tǒng)剩余內(nèi)存位于內(nèi)存臨界值中的一個范圍內(nèi)時,如果一個進(jìn)程的oom_adj值大于或等于這個臨界值對應(yīng)的oom_adj值就會被殺掉。
使用命令:cat /sys/module/lowmemorykiller/parameters/minfree來查看某個手機(jī)的內(nèi)存閾值
18432,23040,27648,32256,36864,46080
注意這些數(shù)字的單位是page. 1 page = 4 kb.上面的六個數(shù)字對應(yīng)的就是(MB): 72,90,108,126,144,180 如數(shù)180代表內(nèi)存低于180M時會清除優(yōu)先級最低的空進(jìn)程。
LMK還維護(hù)著一個管理系統(tǒng)中所有進(jìn)程及其adj信息的雙向鏈表數(shù)組,這個雙向鏈表數(shù)組的每一個元素都是一個雙向鏈表,一個數(shù)組元素中的雙向鏈表里面的元素,都是adj相同的進(jìn)程。 在系統(tǒng)可用內(nèi)存較低時,就會選擇性殺死進(jìn)程的策略。防止內(nèi)存過低影響系統(tǒng)運行。 LMK殺死進(jìn)程的兩個指標(biāo): 1.oom_adj 2.內(nèi)存占用大小
而AMS通過四大組件的運行狀態(tài)更新這些組件相關(guān)聯(lián)的進(jìn)程的oom_adj(包括adj,proc_state,schedule_group等值), AMS計算好每個進(jìn)程的oom_adj,通過socket向lmkd服務(wù)發(fā)送請求,讓lmkd去更新進(jìn)程的優(yōu)先級,lmkd收到請求后,會通過/proc文件系統(tǒng)去更新內(nèi)核中的進(jìn)程優(yōu)先級。這樣AMS就可以間接通過LMK實現(xiàn)對進(jìn)程的動態(tài)管理。
LMKD與AMS交互圖:

有了上面的基礎(chǔ),我們再來具體看下updateOomAdjLocked是如何進(jìn)行動態(tài)更新adj的。
6.關(guān)鍵方法詳解
前面說過,當(dāng)AMS需要更新進(jìn)程的優(yōu)先級時,就會調(diào)用它的updateOomAdjLocked方法,這里只提取方法的updateOomAdjLocked的一些核心代碼:
final void updateOomAdjLocked() {
//省略。。。
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
//...
applyOomAdjLocked(app, true, now, nowElapsed);
//...
}
}
}
可以看到updateOomAdjLocked內(nèi)部主要是對LUR進(jìn)程列表中的每個進(jìn)程調(diào)用computeOomAdjLocked以及applyOomAdjLocked處理
核心方法:computeOomAdjLocked以及applyOomAdjLocked
- 1.computeOomAdjLocked:計算adj,返回計算后RawAdj值
- 2.applyOomAdjLocked:將計算后的adj寫入lmkd,當(dāng)需要殺掉目標(biāo)進(jìn)程則返回false;否則返回true。
computeOomAdjLocked:
該方法會傳入需要更新adj的進(jìn)程描述符ProcessRecord,然后根據(jù)參數(shù)計算出當(dāng)前進(jìn)程甚至關(guān)聯(lián)客戶端進(jìn)程的優(yōu)先級,進(jìn)程狀態(tài),進(jìn)程組等信息。
由于這個方法較長,這里列出代碼流程。
- 1.通過mAdjSeq字段判斷此輪更新是否已經(jīng)計算過adj,是的話直接返回當(dāng)前app.curRawAdj
- 2.判斷進(jìn)程的客戶端線程是否存在,不存在,則:將adj設(shè)置為CACHED_APP_MAX_ADJ。
- 3.判斷是否是前臺進(jìn)程,如果不是:則根據(jù)TOP_APP,app.hasTopUi,activitiesSize,systemNoUi等參數(shù)計算adj。
- 4.前臺進(jìn)程繼續(xù)往下,初始化一些前臺進(jìn)程相關(guān)的默認(rèn)值,后續(xù)再根據(jù)具體情況細(xì)化。
- 5.根據(jù)是否為TOP_APP,是否有正在接受的動畫,是否有正在執(zhí)行的服務(wù),是否有正在運行的Activity以及Activity的狀態(tài)等對adj等參數(shù)賦值。
- 6.對可見進(jìn)程或者擁有可感知的前臺服務(wù)或者后臺服務(wù)等參數(shù)設(shè)置adj
- 7.對后臺進(jìn)程設(shè)置優(yōu)先級
- 8.遍歷在進(jìn)程上運行的Service,根據(jù)Service的狀態(tài)進(jìn)一步更新adj等值。
- 9.同Service。遍歷進(jìn)程上的ContentProvider,根據(jù)ContentProvider的狀態(tài)進(jìn)一步更新adj等值。
- 10.根據(jù)cache進(jìn)程運行狀態(tài),細(xì)分出cache進(jìn)程還有empty進(jìn)程。
- 11.將計算好的adj等值賦值給對應(yīng)的進(jìn)程屬性
代碼就不列出來了,筆者根據(jù)代碼,畫了個流程圖,方便大家查看,感興趣的可以根據(jù)這個圖自行去閱讀源碼。

applyOomAdjLocked:
這個方法主要有三個作用:
- 1.設(shè)置進(jìn)程優(yōu)先級:將前面計算好的curAdj傳遞給LMKD服務(wù)
- 2.設(shè)置進(jìn)程狀態(tài):將curProcState線程狀態(tài)回傳給應(yīng)用進(jìn)程ApplicationThread
- 3.設(shè)置進(jìn)程的調(diào)度策略:將schedGroup設(shè)置為對應(yīng)的進(jìn)程調(diào)度組。
1.設(shè)置進(jìn)程優(yōu)先級
在applyOomAdjLocked方法中比較重要的一段代碼:
if (app.curAdj != app.setAdj) {
ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj);
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
"Set " + app.pid + " " + app.processName + " adj " + app.curAdj + ": "
+ app.adjType);
app.setAdj = app.curAdj;
app.verifiedAdj = ProcessList.INVALID_ADJ;
}
繼續(xù)看ProcessList的setOomAdj方法:
public static final void setOomAdj(int pid, int uid, int amt) {
if (amt == UNKNOWN_ADJ)
return;
long start = SystemClock.elapsedRealtime();
ByteBuffer buf = ByteBuffer.allocate(4 * 4);
buf.putInt(LMK_PROCPRIO);
buf.putInt(pid);
buf.putInt(uid);
buf.putInt(amt);
writeLmkd(buf);
long now = SystemClock.elapsedRealtime();
if ((now-start) > 250) {
Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
+ " = " + amt);
}
}
private static void writeLmkd(ByteBuffer buf) {
for (int i = 0; i < 3; i++) {
if (sLmkdSocket == null) {
if (openLmkdSocket() == false) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
continue;
}
}
try {
sLmkdOutputStream.write(buf.array(), 0, buf.position());
return;
} catch (IOException ex) {
Slog.w(TAG, "Error writing to lowmemorykiller socket");
try {
sLmkdSocket.close();
} catch (IOException ex2) {
}
sLmkdSocket = null;
}
}
}
private static boolean openLmkdSocket() {
try {
sLmkdSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
sLmkdSocket.connect(
new LocalSocketAddress("lmkd",
LocalSocketAddress.Namespace.RESERVED));
sLmkdOutputStream = sLmkdSocket.getOutputStream();
} catch (IOException ex) {
Slog.w(TAG, "lowmemorykiller daemon socket open failed");
sLmkdSocket = null;
return false;
}
return true;
}
可以看到最終將adj,pid,uid寫入名為lmkd的Socket通道中。之后的進(jìn)程adj更新就是由lmkd來負(fù)責(zé)了。 lmkd根據(jù)傳入的參數(shù),去Proc文件系統(tǒng)中更新進(jìn)程優(yōu)先級信息。
2.設(shè)置進(jìn)程狀態(tài)
代碼片段:
if (app.repProcState != app.curProcState) {
app.repProcState = app.curProcState;
if (app.thread != null) {
try {
app.thread.setProcessState(app.repProcState);
} catch (RemoteException e) {
}
}
}
這里調(diào)用了應(yīng)用進(jìn)程的ApplicationThread的setProcessState方法:
public void setProcessState(int state) {
updateProcessState(state, true);
}
public void updateProcessState(int processState, boolean fromIpc) {
synchronized (this) {
if (mLastProcessState != processState) {
mLastProcessState = processState;
// Update Dalvik state based on ActivityManager.PROCESS_STATE_* constants.
final int DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE = 0;
final int DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1;
int dalvikProcessState = DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE;
// TODO: Tune this since things like gmail sync are important background but not jank perceptible.
if (processState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
dalvikProcessState = DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE;
}
VMRuntime.getRuntime().updateProcessState(dalvikProcessState);
if (false) {
Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState
+ (fromIpc ? " (from ipc": ""));
}
}
}
}
ApplicationThread的setProcessState方法: 判斷當(dāng)前processState是否小余或等于ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND狀態(tài)值,將其改為虛擬機(jī)運行時環(huán)境可以識別的DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE值。 最終調(diào)用到了VMRuntime.getRuntime().updateProcessState(dalvikProcessState),將狀態(tài)設(shè)置到AndroidRuntime運行時環(huán)境中。這里其實就是告訴ART運行時當(dāng)前進(jìn)程的可感知能力, 用來切換虛擬機(jī)之間的GC算法,即到底是前臺進(jìn)程GC還是后臺進(jìn)程GC,前臺GC算法效率高,但是會產(chǎn)生碎片,后臺GC效率低,但是不會產(chǎn)生碎片。
具體可以參考下面這篇文章: [ART運行時Foreground GC和Background GC切換過程分析](羅生陽)
3.設(shè)置進(jìn)程調(diào)度策略
if (app.setSchedGroup != app.curSchedGroup) {
int oldSchedGroup = app.setSchedGroup;
app.setSchedGroup = app.curSchedGroup;
switch (app.curSchedGroup) {
case ProcessList.SCHED_GROUP_BACKGROUND:
processGroup = THREAD_GROUP_BG_NONINTERACTIVE;
break;
case ProcessList.SCHED_GROUP_TOP_APP:
case ProcessList.SCHED_GROUP_TOP_APP_BOUND:
processGroup = THREAD_GROUP_TOP_APP;
break;
default:
processGroup = THREAD_GROUP_DEFAULT;
break;
}
long oldId = Binder.clearCallingIdentity();
try {
Process.setProcessGroup(app.pid, processGroup); //1
if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) {
// do nothing if we already switched to RT
if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) {
mVrController.onTopProcChangedLocked(app);
if (mUseFifoUiScheduling) {
//...
} else {
// Boost priority for top app UI and render threads
setThreadPriority(app.pid, TOP_APP_PRIORITY_BOOST);//2
if (app.renderThreadTid != 0) {
try {
setThreadPriority(app.renderThreadTid,
TOP_APP_PRIORITY_BOOST);
} catch (IllegalArgumentException e) {
// thread died, ignore
}
}
}
}
}
} catch (Exception e) {
}
}
這段代碼主要做了兩件事情:
- 1.調(diào)用Process.setProcessGroup(int pid, int group)去設(shè)置進(jìn)程調(diào)度策略,原理就是: 利用linux的cgroup機(jī)制,根據(jù)進(jìn)程狀態(tài)將進(jìn)程放入預(yù)先設(shè)定的cgroup分組中,分組中包含了對cpu使用率、cpuset、cpu調(diào)頻等子資源的配置,以滿足特定狀態(tài)進(jìn)程對系統(tǒng)資源的需求。
- 2.對schedGroup在某前臺和后臺之間切換時,調(diào)用setThreadPriority方法,切換主線程以及繪制線程的優(yōu)先級,以提高用戶的響應(yīng)速度。
總結(jié)
這篇文章主要講解了關(guān)于Android系統(tǒng)中常見的進(jìn)程管理相關(guān)的知識點: 其中對AMS中兩個比較常見的方法:updateLruProcessLocked以及updateOomAdjLocked做了詳細(xì)介紹。
作為應(yīng)用開發(fā)可能我們平時用不到這些,但是在做一些性能優(yōu)化,進(jìn)程?;?/code>的操作時,這些儲備知識卻是必備的。一些高階用法,需要你去了解更深層次的東西,而不僅局限于表面
希望這篇文章對你有幫助。如果你想了解更多關(guān)于Framework的知識請關(guān)注我、
作者:小余的自習(xí)室
鏈接:https://juejin.cn/post/7174713775944138809