本篇文章是基于Android9.0來(lái)介紹Android系統(tǒng)關(guān)于進(jìn)程的優(yōu)先級(jí)是如何定義和管理的。
概述
1.進(jìn)程
進(jìn)程-Process 是程序的一個(gè)運(yùn)行實(shí)例缆娃。通常會(huì)有唯一一個(gè)pid與之對(duì)應(yīng)跑芳。但pid不是絕對(duì)唯一的模暗,當(dāng)進(jìn)程死亡后pid會(huì)被回收給另外的進(jìn)程使用斑粱。在Android世界里三热,App開(kāi)發(fā)者很容易認(rèn)為系統(tǒng)的四大組件就是進(jìn)程的載體,實(shí)際上三幻,它們不能算是完整的進(jìn)程實(shí)例就漾,最多只能算是進(jìn)程的組成部分。由于Android系統(tǒng)框架中念搬,系統(tǒng)對(duì)進(jìn)程的創(chuàng)建和管理進(jìn)行了封裝抑堡,每當(dāng)我們?cè)趩?dòng)四大組件Activity, BroadcastReceiver,ContentProvider,Service任意一個(gè)的時(shí)候,系統(tǒng)就會(huì)去檢查該組件所在的進(jìn)程是否已經(jīng)存在朗徊,如果不存在首妖,框架就會(huì)自動(dòng)調(diào)用startProcessLocked函數(shù)去創(chuàng)建進(jìn)程。當(dāng)然一個(gè)app可以存在多個(gè)進(jìn)程爷恳,多個(gè)app也可以運(yùn)行在同一個(gè)進(jìn)程下有缆,我們可以通過(guò)Android:process來(lái)設(shè)置。
2.優(yōu)先級(jí)
Android系統(tǒng)框架設(shè)計(jì)理念里是希望對(duì)用戶很重要的進(jìn)程盡可能的長(zhǎng)期存活温亲,以此來(lái)提高用戶體驗(yàn)棚壁。我們知道Android app在啟動(dòng)的時(shí)候會(huì)去做很多事情,比如系統(tǒng)會(huì)檢測(cè)該app進(jìn)程是否存在栈虚,如果不存在則需要通知zygote進(jìn)程去fork app進(jìn)程袖外,同時(shí)完成application信息的初始化工作。此時(shí)如果app的進(jìn)程存在魂务,那么就會(huì)省略這個(gè)步驟曼验,直接快速喚起app。對(duì)于app來(lái)說(shuō)也是一樣粘姜,通過(guò)進(jìn)程摈拚眨活可以去實(shí)現(xiàn)更多的功能,但是如果所有的app不管有用沒(méi)用的都存活下來(lái)了相艇,系統(tǒng)的資源畢竟是有限的颖杏,系統(tǒng)內(nèi)存很快就會(huì)枯竭而亡,這時(shí)就需要有合理地進(jìn)程回收機(jī)制了坛芽。那么究竟該怎么回收進(jìn)程呢留储?其實(shí)系統(tǒng)是根據(jù)各個(gè)app四大組件的狀態(tài)來(lái)決定進(jìn)程的優(yōu)先級(jí)值adj翼抠。系統(tǒng)會(huì)根據(jù)一定的策略先去回收優(yōu)先級(jí)最低的(同時(shí)也是adj值最大的),其次再回收優(yōu)先級(jí)略低的获讳,依次類(lèi)推阴颖,直到回收了足夠的系統(tǒng)資源,保證系統(tǒng)正常運(yùn)轉(zhuǎn)丐膝。
3.adj取值范圍及含義
ADJ級(jí)別 | 取值 | 含義 |
---|---|---|
NATIVE_ADJ | -1000 | native進(jìn)程 |
SYSTEM_ADJ | -900 | 僅指system_server進(jìn)程 |
PERSISTENT_PROC_ADJ | -800 | 系統(tǒng)persistent進(jìn)程 |
PERSISTENT_SERVICE_ADJ | -700 | 關(guān)聯(lián)著系統(tǒng)或persistent進(jìn)程 |
FOREGROUND_APP_ADJ | 0 | 前臺(tái)進(jìn)程 |
VISIBLE_APP_ADJ | 100 | 可見(jiàn)進(jìn)程 |
PERCEPTIBLE_APP_ADJ | 200 | 可感知進(jìn)程量愧,比如后臺(tái)音樂(lè)播放 |
BACKUP_APP_ADJ | 300 | 備份進(jìn)程 |
HEAVY_WEIGHT_APP_ADJ | 400 | 重量級(jí)進(jìn)程 |
SERVICE_ADJ | 500 | 服務(wù)進(jìn)程 |
HOME_APP_ADJ | 600 | Home進(jìn)程 |
PREVIOUS_APP_ADJ | 700 | 上一個(gè)進(jìn)程 |
SERVICE_B_ADJ | 800 | B List中的Service |
CACHED_APP_MIN_ADJ | 900 | 不可見(jiàn)進(jìn)程的adj最小值 |
CACHED_APP_MAX_ADJ | 906 | 不可見(jiàn)進(jìn)程的adj最大值 |
從Android 7.0開(kāi)始,ADJ采用100帅矗、200偎肃、300;在這之前的版本ADJ采用數(shù)字1浑此、2累颂、3,這樣的調(diào)整可以更進(jìn)一步地細(xì)化進(jìn)程的優(yōu)先級(jí)凛俱,比如在VISIBLE_APP_ADJ(100)與PERCEPTIBLE_APP_ADJ(200)之間紊馏,可以有ADJ=101、102級(jí)別的進(jìn)程蒲犬。
關(guān)于進(jìn)程的優(yōu)先級(jí)可以通過(guò)如下的命令查看
adb shell
cd /proc/pid/
oom_adj oom_score oom_score_adj
cat oom_adj
0 表示前臺(tái)進(jìn)程FOREGROUND_APP_ADJ
Android 7.0之前的版本:
oom_score -- 是該進(jìn)程的最終得分朱监,分?jǐn)?shù)越高,越容易被殺死原叮;
oom_score_adj -- 范圍:[-1000, 1000]赫编,是kernel用來(lái)配置進(jìn)程優(yōu)先級(jí)的。值越低篇裁,最終的oom_score越低沛慢。
oom_adj -- 范圍:[-16, 15],是給用戶來(lái)配置進(jìn)程優(yōu)先級(jí)的达布。為了方便用戶配置团甲,提供了范圍更小的oom_adj參數(shù),數(shù)字越小優(yōu)先級(jí)越高黍聂,-17表示該進(jìn)程被保護(hù)躺苦,不被OOMKiller殺死。
kernel會(huì)轉(zhuǎn)換并更新該進(jìn)程實(shí)際的oom_score_adj值产还,它們的換算關(guān)系是:
#define OOM_DISABLE (-17)
#define OOM_SCORE_ADJ_MAX 1000
oom_score_adj = (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
oom_adj = (oom_score_adj * - OOM_DISABLE) / OOM_SCORE_ADJ_MAX;
Android 7.0之前的版本匹厘,oom_score_adj= oom_adj * 1000/17; 而Android 7.0開(kāi)始脐区,oom_score_adj= oom_adj愈诚,不用再經(jīng)過(guò)一次轉(zhuǎn)換。
4.LowMemoryKiller
Android的Low Memory Killer基于Linux的OOM(Out Of Memory Killer)機(jī)制,在Linux中炕柔,內(nèi)存是以頁(yè)面為單位分配的酌泰,OOM的策略更多的是用于分配內(nèi)存不足時(shí)觸發(fā),將得分最高的進(jìn)程殺掉匕累。而lmk則會(huì)每隔一段時(shí)間檢查一次陵刹,當(dāng)系統(tǒng)剩余可用內(nèi)存較低時(shí),便會(huì)觸發(fā)殺進(jìn)程的策略欢嘿,根據(jù)不同的剩余內(nèi)存檔位來(lái)選擇殺不同優(yōu)先級(jí)的進(jìn)程衰琐,而不是等到OOM時(shí)再來(lái)殺進(jìn)程,真正OOM時(shí)系統(tǒng)可能已經(jīng)處于異常狀態(tài)炼蹦,系統(tǒng)更希望的是未雨綢繆羡宙,在內(nèi)存很低時(shí)來(lái)殺掉一些優(yōu)先級(jí)較低的進(jìn)程來(lái)保障后續(xù)操作的順利進(jìn)行。這里不詳細(xì)說(shuō)掐隐,接下來(lái)會(huì)有一篇關(guān)于Low Memory Killer的文章來(lái)詳細(xì)介紹辛辨。
那么在哪里可以看到各個(gè)剩余內(nèi)存檔位呢?可以通過(guò)如下方式:
frameworks/base/services/core/java/com/android/server/am/ProcessList.java
// These are the various interesting memory levels that we will give to
// the OOM killer. Note that the OOM killer only supports 6 slots, so we
// can't give it a different value for every possible kind of process.
private final int[] mOomAdj = new int[] {
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
};
從ProcessList.java中關(guān)于mOomAdj的定義及注釋中我們可以發(fā)現(xiàn)瑟枫,
系統(tǒng)一共定義了6個(gè)檔位,
分別對(duì)應(yīng)了上表格中列出的幾種進(jìn)程類(lèi)型指攒。
adb shell
cat /sys/module/lowmemorykiller/parameters/adj
0,100,200,300,900,906
adj值對(duì)應(yīng)如下:
FOREGROUND_APP_ADJ(0)
VISIBLE_APP_ADJ(100)
PERCEPTIBLE_APP_ADJ(200)
BACKUP_APP_ADJ(300)
CACHED_APP_MIN_ADJ(900)
CACHED_APP_MAX_ADJ(906)
cat /sys/module/lowmemorykiller/parameters/minfree
18432,23040,27648,32256,85296,120640
由于一個(gè)page=4k,對(duì)應(yīng)的換算公式是:value*4/1024,換算結(jié)果如下:
72M,90M,108M,126M,144M,180M
剩余內(nèi)存值檔位對(duì)應(yīng)如下:
FOREGROUND_APP_ADJ(72M)
VISIBLE_APP_ADJ(90M)
PERCEPTIBLE_APP_ADJ(108M)
BACKUP_APP_ADJ(126M)
CACHED_APP_MIN_ADJ(144M)
CACHED_APP_MAX_ADJ(180M)
進(jìn)程剛啟動(dòng)時(shí)ADJ等于INVALID_ADJ慷妙,當(dāng)執(zhí)行完attachApplication(),該該進(jìn)程的curAdj和setAdj不相等允悦,則會(huì)觸發(fā)執(zhí)行setOomAdj()將該進(jìn)程的節(jié)點(diǎn)/proc/pid/oom_score_adj寫(xiě)入oomadj值膝擂。舉例:當(dāng)系統(tǒng)剩余空閑內(nèi)存低于某閾值(比如140MB),則從ADJ大于或等于相應(yīng)閾值(比如900)的進(jìn)程中隙弛,選擇ADJ值最大的進(jìn)程架馋,如果存在多個(gè)ADJ相同的進(jìn)程,則選擇內(nèi)存最大的進(jìn)程全闷。
在updateOomLevels()過(guò)程叉寂,會(huì)根據(jù)手機(jī)屏幕尺寸或內(nèi)存大小來(lái)調(diào)整scale,默認(rèn)大多數(shù)手機(jī)內(nèi)存都大于700MB总珠,則scale等于1屏鳍。對(duì)于64位手機(jī),cached進(jìn)程的閾值會(huì)更大些局服。
private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
// Scale buckets from avail memory: at 300MB we use the lowest values to
// 700MB or more for the top values.
float scaleMem = ((float)(mTotalMemMb-350))/(700-350);
...
// Scale buckets from screen size.
int minSize = 480*800; // 384000
int maxSize = 1280*800; // 1024000 230400 870400 .264
float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
...
float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;
if (scale < 0) scale = 0;
else if (scale > 1) scale = 1;
...
final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
for (int i=0; i<mOomAdj.length; i++) {
int low = mOomMinFreeLow[i];
int high = mOomMinFreeHigh[i];
if (is64bit) {
// Increase the high min-free levels for cached processes for 64-bit
if (i == 4) high = (high*3)/2;
else if (i == 5) high = (high*7)/4;
}
mOomMinFree[i] = (int)(low + ((high-low)*scale));
}
}
ADJ詳解
1.ADJ優(yōu)先級(jí)小于0的钓瞭,就是說(shuō)正常情況下肯定不會(huì)被殺死,即使被殺死了也會(huì)被重啟的進(jìn)程淫奔。
- NATIVE_ADJ(-1000):是由init進(jìn)程fork出來(lái)的Native進(jìn)程山涡,并不受system管控
- SYSTEM_ADJ(-900):是指system_server進(jìn)程
- PERSISTENT_PROC_ADJ(-800): 是指在AndroidManifest.xml中申明android:persistent=”true”的系統(tǒng)(即帶有FLAG_SYSTEM標(biāo)記)進(jìn)程,persistent進(jìn)程一般情況并不會(huì)被殺,即便被殺或者發(fā)生Crash系統(tǒng)會(huì)立即重新拉起該進(jìn)程
- PERSISTENT_SERVICE_ADJ(-700):是由startIsolatedProcess()方式啟動(dòng)的進(jìn)程鸭丛,或者是由system_server或者persistent進(jìn)程所綁定(并且?guī)в蠦IND_ABOVE_CLIENT或者BIND_IMPORTANT)的服務(wù)進(jìn)程
2.ADJ優(yōu)先級(jí)大于等于0的
-
FOREGROUND_APP_ADJ(0): 正在前臺(tái)運(yùn)行的進(jìn)程竞穷,是不應(yīng)該被干掉的進(jìn)程
1.正處于resumed狀態(tài)的Activity; app.adjType = "top-activity"
2.正執(zhí)行一個(gè)生命周期回調(diào)的Service(比如執(zhí)行onCreate,onStartCommand,onDestroy等); app.adjType = "exec-service";
3.正執(zhí)行onReceive()的BroadcastReceiver; app.adjType = "broadcast";
4.通過(guò)startInstrumentation()啟動(dòng)的進(jìn)程; app.adjType = "instrumentation"
5.當(dāng)客戶端進(jìn)程activity里面調(diào)用bindService()方法時(shí)flags帶有BIND_ADJUST_WITH_ACTIVITY參數(shù),并且該activity處于可見(jiàn)狀態(tài)系吩,則當(dāng)前服務(wù)進(jìn)程也屬于前臺(tái)進(jìn)程; app.adjType = "service";
6.對(duì)于provider進(jìn)程来庭,還有以下兩個(gè)條件能成為前臺(tái)進(jìn)程:- 當(dāng)Provider的客戶端進(jìn)程ADJ<=FOREGROUND_APP_ADJ時(shí),則Provider進(jìn)程ADJ等于FOREGROUND_APP_ADJ; adjType = "provider";
- 當(dāng)Provider有外部進(jìn)程依賴(lài)穿挨,也就是調(diào)用了getContentProviderExternal()方法月弛,則ADJ至少等于FOREGROUND_APP_ADJ; app.adjType = "ext-provider";
VISIBLE_APP_ADJ(100): 可見(jiàn)進(jìn)程,比如生命周期還沒(méi)有執(zhí)行到onstop的activity,有widget組件在桌面的等還是可以被用戶看到的; app.adjType = "vis-activity";
從Android P開(kāi)始科盛,進(jìn)一步細(xì)化ADJ級(jí)別帽衙,增加了VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1=99,是指VISIBLE_APP_ADJ(100)跟PERCEPTIBLE_APP_ADJ(200)之間有99個(gè)槽贞绵,則可見(jiàn)級(jí)別ADJ的取值范圍為[100,199]厉萝。 算法會(huì)根據(jù)其所在task的mLayerRank來(lái)調(diào)整其ADJ,100加上mLayerRank就等于目標(biāo)ADJ榨崩,layer越大谴垫,則ADJ越小。
關(guān)于TaskRecord的mLayerRank的計(jì)算方式是在updateOomAdjLocked()過(guò)程調(diào)用ASS的rankTaskLayersIfNeeded()方法PERCEPTIBLE_APP_ADJ(200): 當(dāng)該進(jìn)程存在不可見(jiàn)的Activity母蛛,但Activity正處于PAUSING翩剪、PAUSED、STOPPING狀態(tài)彩郊,則為PERCEPTIBLE_APP_ADJ前弯;app.adjType = "pause-activity" or app.adjType = "stop-activity";
滿足以下任一條件的進(jìn)程也屬于可感知進(jìn)程:
1.foregroundServices非空:前臺(tái)服務(wù)進(jìn)程,執(zhí)行startForegroundService()方法; app.adjType = "fg-service";
2.app.forcingToImportant非空:執(zhí)行setProcessImportant()方法秫逝,比如Toast彈出過(guò)程; app.adjType = "force-imp";
3.hasOverlayUi非空:非activity的UI位于屏幕最頂層恕出,比如顯示類(lèi)型TYPE_APPLICATION_OVERLAY的窗口; app.adjType = "has-overlay-ui";BACKUP_APP_ADJ(300):執(zhí)行bindBackupAgent()過(guò)程的進(jìn)程;
1.執(zhí)行bindBackupAgent()過(guò)程,設(shè)置mBackupTarget值违帆;
2.執(zhí)行clearPendingBackup()或unbindBackupAgent()過(guò)程浙巫,置空mBackupTarget值;HEAVY_WEIGHT_APP_ADJ(400): realStartActivityLocked()過(guò)程前方,當(dāng)應(yīng)用的privateFlags標(biāo)識(shí)PRIVATE_FLAG_CANT_SAVE_STATE的進(jìn)程狈醉;
HEAVY_WEIGHT_APP一般是在后臺(tái)運(yùn)行,要避免它被干掉惠险。Value set in system/rootdir/init.rc on startup.SERVICE_ADJ(500):沒(méi)有啟動(dòng)過(guò)Activity苗傅,并且30分鐘之內(nèi)活躍過(guò)的服務(wù)進(jìn)程。startRequested為true班巩,則代表執(zhí)行startService()且沒(méi)有stop的進(jìn)程渣慕; app.adjType = "started-services";
HOME_APP_ADJ(600):當(dāng)類(lèi)型為ACTIVITY_TYPE_HOME的應(yīng)用嘶炭,比如桌面APP
PREVIOUS_APP_ADJ(700):
1.用戶上一個(gè)使用的包含UI的進(jìn)程,為了給用戶在兩個(gè)APP之間更好的切換體驗(yàn)逊桦,將上一個(gè)進(jìn)程ADJ設(shè)置到PREVIOUS_APP_ADJ的檔次眨猎。 當(dāng)activityStoppedLocked()過(guò)程會(huì)更新上一個(gè)應(yīng)用。
2.當(dāng)provider進(jìn)程强经,上一次使用時(shí)間不超過(guò)20S的情況下睡陪,優(yōu)先級(jí)不低于PREVIOUS_APP_ADJ。provider進(jìn)程這個(gè)是Android 7.0以后新增的邏輯 匿情,這樣做的好處是在內(nèi)存比較低的情況下避免擁有provider的進(jìn)程出現(xiàn)顛簸兰迫,也就是啟動(dòng)后殺,然后又被拉炬称。SERVICE_B_ADJ(800):進(jìn)程由SERVICE_ADJ(500)降低到SERVICE_B_ADJ(800)汁果,有以下兩種情況:
1.A類(lèi)Service占比過(guò)高:當(dāng)A類(lèi)Service個(gè)數(shù) > Service總數(shù)的1/3時(shí),則加入到B類(lèi)Service玲躯。換句話說(shuō)据德,B Service的個(gè)數(shù)至少是A Service的2倍。
2.內(nèi)存緊張&&A類(lèi)Service占用內(nèi)存較高:當(dāng)系統(tǒng)內(nèi)存緊張級(jí)別(mLastMemoryLevel)高于ADJ_MEM_FACTOR_NORMAL跷车,且該應(yīng)用所占內(nèi)存lastPss大于或等于CACHED_APP_MAX_ADJ級(jí)別所對(duì)應(yīng)的內(nèi)存閾值的1/3(默認(rèn)值閾值約等于110MB)
內(nèi)存因子ADJ_MEM_FACTOR共有4個(gè)級(jí)別棘利,當(dāng)前處于哪個(gè)內(nèi)存因子級(jí)別,取決于當(dāng)前進(jìn)程中cached進(jìn)程和空進(jìn)程的個(gè)數(shù)
final int numCachedAndEmpty = numCached + numEmpty;
int memFactor;
if (numCached <= mConstants.CUR_TRIM_CACHED_PROCESSES
&& numEmpty <= mConstants.CUR_TRIM_EMPTY_PROCESSES) {
if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
} else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
} else {
memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
}
} else {
memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
}
內(nèi)存因子 | 取值 | 先決條件 |
---|---|---|
ADJ_MEM_FACTOR_CRITICAL | 3 | Cached+Empty<=3 |
ADJ_MEM_FACTOR_LOW | 2 | Cached+Empty<=5 |
ADJ_MEM_FACTOR_MODERATE | 1 | Cached<=5 && Empty<=8 |
ADJ_MEM_FACTOR_NORMAL | 0 | Cached>5或者Empty>8 |
- 最大緩存進(jìn)程個(gè)數(shù):CUR_MAX_CACHED_PROCESSES = MAX_CACHED_PROCESSES = 32
- 最大空進(jìn)程個(gè)數(shù): CUR_MAX_EMPTY_PROCESSES = MAX_CACHED_PROCESSES/2 = 16
- Trim空進(jìn)程上限:CUR_TRIM_EMPTY_PROCESSES = MAX_CACHED_PROCESSES/4 = 8
- Trim緩存進(jìn)程上限:CUR_TRIM_CACHED_PROCESSES = MAX_CACHED_PROCESSES/6 = 5
注:用于限制empty或cached進(jìn)程的上限為16個(gè)朽缴,
并且empty超過(guò)8個(gè)時(shí)會(huì)清理掉30分鐘沒(méi)有活躍的進(jìn)程赡译。 cached和empty主要是區(qū)別是否有Activity
系統(tǒng)會(huì)有相應(yīng)的log輸出:
//默認(rèn)cachedProcessLimit=16
if (numCached > cachedProcessLimit) {
app.kill("cached #" + numCached, true);
}
//默認(rèn)CUR_TRIM_EMPTY_PROCESSES=8, 且滿足30min
if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES
&& app.lastActivityTime < oldTime) {
app.kill("empty for "+ (
(oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime)
/ 1000) + "s", true);
}
//默認(rèn)cachedProcessLimit=16
if (numEmpty > emptyProcessLimit) {
app.kill("empty #" + numEmpty, true);
}
-
CACHED_APP_MIN_ADJ(900):
緩存進(jìn)程優(yōu)先級(jí)從CACHED_APP_MIN_ADJ(900)到 CACHED_APP_MAX_ADJ(906)。
ADJ的轉(zhuǎn)換算法:
cached: 900, 901, 903, 905
empty: 900, 902, 904, 906
cache process
1.foregroundActivities代表當(dāng)前不是前臺(tái)(FOREGROUND_APP_ADJ)進(jìn)程不铆,并且存在Activity的進(jìn)程,當(dāng)該Activity窗口不可見(jiàn)裹唆,并且不處于PAUSING(正在)誓斥、PAUSED(onPause個(gè))、STOPPING的任一狀態(tài)的情況下许帐,則設(shè)置該進(jìn)程為PROCESS_STATE_CACHED_ACTIVITY劳坑。
app.adjType = "cch-act";
2.當(dāng)該進(jìn)程Service的客戶端進(jìn)程存在Activity或者是treatLikeActivity的進(jìn)程,其進(jìn)程狀態(tài)都是cached進(jìn)程成畦。
app.adjType = "cch-as-act";
app.adjType = "cch-client-act";
總結(jié)
Android進(jìn)程優(yōu)先級(jí)ADJ的每一個(gè)ADJ級(jí)別往往都有多種場(chǎng)景距芬,使用adjType完美地區(qū)分相同ADJ下的不同場(chǎng)景; 不同ADJ進(jìn)程所對(duì)應(yīng)的schedGroup不同循帐,從而分配的CPU資源也不同框仔,schedGroup大體分為T(mén)OP(T)、前臺(tái)(F)拄养、后臺(tái)(B)离斩; ADJ跟AMS中的procState有著緊密的聯(lián)系。
adj:通過(guò)調(diào)整oom_score_adj來(lái)影響進(jìn)程壽命(Lowmemorykiller殺進(jìn)程策略);
schedGroup:影響進(jìn)程的CPU資源調(diào)度與分配跛梗;
procState:從進(jìn)程所包含的四大組件運(yùn)行狀態(tài)來(lái)評(píng)估進(jìn)程狀態(tài)寻馏,影響framework的內(nèi)存控制策略。比如控制緩存進(jìn)程和空進(jìn)程個(gè)數(shù)上限依賴(lài)于procState核偿,再比如控制APP執(zhí)行handleLowMemory()的觸發(fā)時(shí)機(jī)等诚欠。
CPU調(diào)度組:
調(diào)度級(jí)別 | 縮寫(xiě) | 解釋 |
---|---|---|
SCHED_GROUP_BACKGROUND(0) | B | 后臺(tái)進(jìn)程組 |
SCHED_GROUP_DEFAULT(1) | F | 前臺(tái)進(jìn)程組 |
SCHED_GROUP_TOP_APP(2) | T | TOP進(jìn)程組 |
SCHED_GROUP_TOP_APP_BOUND(3) | T | TOP進(jìn)程組 |
- 常說(shuō)的前臺(tái)進(jìn)程與后臺(tái)進(jìn)程,其實(shí)是從CPU調(diào)度角度來(lái)劃分的前臺(tái)與后臺(tái)漾岳;為了讓用戶正在使用的TOP進(jìn)程能分配到更多的CPU資源轰绵,從Android 6.0開(kāi)始新增了TOP進(jìn)程組,CPU調(diào)度優(yōu)先分配給當(dāng)前正在跟用戶交互的APP蝗羊,提升用戶體驗(yàn)藏澳。
- 上圖adjType=”broadcast”的CPU調(diào)度組的選擇取決于廣播隊(duì)列,當(dāng)receiver接收到的廣播來(lái)自于前臺(tái)廣播隊(duì)列則采用前臺(tái)進(jìn)程組耀找,當(dāng)receiver接收到的廣播來(lái)自于后臺(tái)廣播隊(duì)列則采用后臺(tái)進(jìn)程組翔悠。前后臺(tái)廣播隊(duì)列的CPU資源調(diào)度優(yōu)先級(jí)不同,所以前臺(tái)廣播超時(shí)10秒就會(huì)ANR野芒,而后臺(tái)廣播超時(shí)60秒才會(huì)ANR蓄愁。更少的CPU資源分配就需要更長(zhǎng)的時(shí)間來(lái)完成執(zhí)行,這也就是為何兩個(gè)廣播隊(duì)列定義了不同的超時(shí)閾值狞悲。
- 上圖adjType=”exec-service”的CPU調(diào)度組的選擇取決于caller, 當(dāng)發(fā)起bindService或者startService的調(diào)用者caller屬于后臺(tái)進(jìn)程組撮抓,callerFg=false,則Service的生命周期回調(diào)運(yùn)行在后臺(tái)進(jìn)程組摇锋,非常少的CPU資源丹拯;當(dāng)caller屬于前臺(tái)或者TOP進(jìn)程組,則Service的生命周期回調(diào)運(yùn)行在前臺(tái)進(jìn)程組荸恕,分配較多的CPU資源乖酬。
- 上圖adjType=”service”也有機(jī)會(huì)選擇TOP組, 前提條件是在bindService的時(shí)候帶有BIND_IMPORTANT的flags,用于標(biāo)記該服務(wù)對(duì)于客戶端進(jìn)程很重要融求。
對(duì)于app開(kāi)發(fā)者的建議:
- UI進(jìn)程與Service進(jìn)程一定要分離咬像,因?yàn)閷?duì)于包含activity的service進(jìn)程,一旦進(jìn)入后臺(tái)就成為”cch-started-ui-services”類(lèi)型的cache進(jìn)程(ADJ>=900)生宛,隨時(shí)可能會(huì)被系統(tǒng)回收县昂;而分離后的Service進(jìn)程服務(wù)屬于SERVICE_ADJ(500),被殺的可能性相對(duì)較小陷舅。尤其是系統(tǒng)允許自啟動(dòng)的服務(wù)進(jìn)程必須做UI分離倒彰,避免消耗系統(tǒng)較大內(nèi)存。
- 只有真正需要用戶可感知的應(yīng)用莱睁,才調(diào)用startForegroundService()方法來(lái)啟動(dòng)前臺(tái)服務(wù)狸驳,此時(shí)ADJ=PERCEPTIBLE_APP_ADJ(200)预明,常駐內(nèi)存,并且會(huì)在通知欄常駐通知提醒用戶耙箍,比如音樂(lè)播放撰糠,地圖導(dǎo)航。切勿為了常駐而濫用前臺(tái)服務(wù)辩昆,這會(huì)嚴(yán)重影響用戶體驗(yàn)阅酪。
- 進(jìn)程中的Service工作完成后,務(wù)必主動(dòng)調(diào)用stopService或stopSelf來(lái)停止服務(wù)汁针,避免占據(jù)內(nèi)存术辐,浪費(fèi)系統(tǒng)資源;
- 不要長(zhǎng)時(shí)間綁定其他進(jìn)程的service或者provider施无,每次使用完成后應(yīng)立刻釋放辉词,避免其他進(jìn)程常駐于內(nèi)存;
- APP應(yīng)該實(shí)現(xiàn)接口onTrimMemory()和onLowMemory()猾骡,根據(jù)TrimLevel適當(dāng)?shù)貙⒎潜仨殐?nèi)存在回調(diào)方法中加以釋放瑞躺。當(dāng)系統(tǒng)內(nèi)存緊張時(shí)會(huì)回調(diào)該接口,減少系統(tǒng)卡頓與殺進(jìn)程頻次兴想。
- 減少在贝鄙冢活上花心思,更應(yīng)該在優(yōu)化內(nèi)存上下功夫嫂便,因?yàn)樵谙嗤珹DJ級(jí)別的情況下捞镰,系統(tǒng)會(huì)選擇優(yōu)先殺內(nèi)存占用的進(jìn)程。
參考:
http://gityuan.com/2018/05/19/android-process-adj/
https://blog.csdn.net/kickxxx/article/details/13996565
https://blog.csdn.net/u012602304/article/details/79066000
https://blog.csdn.net/sinat_34606064/article/details/77932268
http://gityuan.com/2016/09/17/android-lowmemorykiller/