目錄
概述
Android系統(tǒng)的進程管理理念是希望應用進程能夠盡量長時間的存活贺喝,提升用戶體驗。Android的應用進程在首次啟動的時候會比較慢死宣,因為第一次啟動的時候包含了進程的創(chuàng)建以及Application等信息的初始化,這個過程會消耗一定的時間,所以應用在啟動以后儒溉,不會輕易被殺死掉,以達到在下一次啟動的時候加快速度发钝。同樣APP自己也會使用一些手段顿涣,希望自己長時間存活。設備的內存以及一些資源是有限的酝豪,不可能承載無限多的進程運行涛碑,當進程達到一定的數(shù)量,大量消耗設備內存后孵淘,手機設備性能就會下降蒲障,如果放任所有進程一直存活下去,設備內存很快就會消耗完畢夺英。
所以系統(tǒng)需要殺死一些進程對內存進行回收晌涕,確保系統(tǒng)可以穩(wěn)定的一直運行下去。但是哪些進程應該被殺掉呢痛悯?Android系統(tǒng)會根據進程中的組件狀態(tài)來決定一個進程的優(yōu)先級adj值余黎,優(yōu)先級最低的進程會最先被殺掉,依次類推载萌,來確保系統(tǒng)正常運轉惧财。
AMS對進程的描述
Android 中AMS服務負責進程的創(chuàng)建和銷毀巡扇,Android系統(tǒng)對用戶屏蔽了進程的概念,用戶在開發(fā)自己的應用的時候無需太關系進程相關的處理垮衷。
進程在AMS中由一個ProcessRecord來表示厅翔,該對象記錄了一個進程的所有信息,但是ProcessRecord在AMS服務中只是代表一個進程搀突,真正的應用進程運行在獨立的進程中刀闷,主線程是ActivityThread。ProcessRecord的變量IApplicationThread thread用來關聯(lián)真正的進程仰迁,IApplicationThread是一個binder類甸昏,當他不為空的時候,thread持有了Binder的代理端徐许,而服務端實現(xiàn)在ActivityThread中施蜜。
AMS可以通過IApplicationThread通知對應的進程做一些對應的操作。
ProcessRecord詳情可參考ProcessRecord分析
AMS服務創(chuàng)建進程
既然AMS負責管理進程雌隅,那么AMS是怎么創(chuàng)建一個進程的呢翻默?在什么場景下會創(chuàng)建新的進程呢?
進程的創(chuàng)建場景
- 啟動Activity的時候恰起,如果當前Activity的宿主進程尚未創(chuàng)建修械,則需要先創(chuàng)建對應的進程
ActivityStackSupervisor::startSpecificActivityLocked()
- 啟動一個Service組件的時候,Service組件的宿主進程尚未創(chuàng)建村缸,需要先創(chuàng)建對應的進程
ActiveServices::bringUpServiceLocked()
- 獲取一個ContentProvider組件連接的時候祠肥,ContentProvider宿主進程尚未創(chuàng)建且ContentProvider組件必須運行在其宿主進程中,需要先創(chuàng)建對應的進程
ActivityManagerService::getContentProviderImpl()
- 如果發(fā)送一個廣播的時候梯皿,發(fā)現(xiàn)接收該廣播的Receiver是靜態(tài)注冊的廣播仇箱,且注冊該廣播的進程尚未啟動,則需要先創(chuàng)建對應的進程
BroadcastQueue::processNextBroadcast()
- backup組件
ActivityManagerService::bindBackupAgent()
- systemServer進程啟動的時候东羹,persistent進程會被啟動
addAppLocked()
進程創(chuàng)建流程
- 從AMS的進程列表中查詢對應的ProcessRecord信息
- 如果AMS進程列表中未找到對應的ProcessRecord信息剂桥,說明進程還未啟動,需要創(chuàng)建新的進程
- 創(chuàng)建一個ProcessRecord來描述要創(chuàng)建進程的信息属提,并進程初始化权逗,保存到AMS的ProcessRecord列表中,此時的ProcessRecord值保存了基本的信息冤议,尚未和一個真正的進程進行關聯(lián)
- 為新啟動進程設置必須的參數(shù)
- 調用Process.start來啟動一個新的進程
- 將參數(shù)通過socket發(fā)送給Zygote進程斟薇,Zygote進程fork出一個新的進程,返回新創(chuàng)建的進程pid
- 將進程的Pid保存到ProcessRecord對象的pid變量中. AMS端創(chuàng)建進程的邏輯就執(zhí)行完成了恕酸。
- Zygote fork出的新進程會執(zhí)行ActivityThread.main方法堪滨,初始化新的進程,此時一個新的進程真正的運行起來了蕊温,同時也創(chuàng)建了一個IApplicationThread的服務端用于接收AMS的消息.
- 新進程調用AMS.attachApplicationLocked方法袱箱,將IApplicationThread的代理端發(fā)送給了AMS, AMS查找到對應的ProcessRecord對象遏乔,將Binder代理端保存到ProcessRecord的thread變量中,此時ProcessRecord對象和一個進程就關聯(lián)起來了发笔。
AMS服務查殺進程
殺死進程的方法有那些盟萨?
方法名稱 | 方法描述 | 調用角色 |
---|---|---|
System.exit(0) | 退出虛擬機 | 應用 |
killProcessGroup(int uid, int pid) | 殺死pid所在進程組內的所有進程 | 系統(tǒng) 應用 |
killPids(int[] pids, String pReason, boolean secure) | 只有系統(tǒng)UID可以調用,根據指定的pid信息計算一個worstType, 小于某一個adj的進程會被殺死了讨,不能保證指定的pid進程一定被殺掉 | 系統(tǒng) |
killUid(int appId, int userId, String reason) | 殺死UID下所有的進程捻激, system_server和native進程除外 | 系統(tǒng) 應用 |
killApplication(String pkg, int appId, int userId, String reason) | 只有系統(tǒng)Uid可以調用方库,強制殺死某一個應用 | 系統(tǒng) |
killApplicationProcess(String processName, int uid) | 系統(tǒng)uid才能調用泣港,通過進程見調用通知進程自殺 | 系統(tǒng) |
killAllBackgroundProcesses() | 殺死所有優(yōu)先級小于CACHE的進程,需要權限 | 系統(tǒng) |
killBackgroundProcesses(final String packageName, int userId) | 殺死指定Package下所有優(yōu)先級小于Service的進程 | 系統(tǒng) |
killProcessesBelowForeground(String reason) | 殺死優(yōu)先級小于FOREGROUND的進程 | 系統(tǒng) |
killProcessesBelowAdj(int belowAdj, String reason) | 殺死優(yōu)先級小于指定值的進程 | 系統(tǒng) |
killPackageDependents(String packageName, int userId) | 殺死指定Package所有優(yōu)先級小于FOREGROUND的進程 | 系統(tǒng) |
killPackageProcessesLocked | 殺死指定package下小于指定優(yōu)先級的進程,非系統(tǒng)UID只能殺死自己進程 | 系統(tǒng) 應用 |
以上方法提供了殺死進程的接口残炮,用于應用或者系統(tǒng)主動殺死一些進程。這些進程會調用一些方法
- handleAppDiedLocked() 進程被殺死后的善后處理
參考binderDied()過程分析 - forceStopPackageLocked() 強制殺死對應進程
AMS自己會根據一些邏輯來殺死部分進程缩滨, App也會主動調用ActivityManager的接口來殺死指定進程势就,當進程僥幸逃脫AMS或者應用的屠刀后,就會被保存到內存中脉漏,此時這些進程就會交由LowMemoryKiller來監(jiān)控了苞冯,這些進程的生死就轉移到了LMK的手上.
LMK進程監(jiān)控查殺
需要監(jiān)控的進程,ProcessList會通過socket將他的進程id添加到lmkd的進程列表中侧巨,同時舅锄,如果進程被殺死了,則需要通ProcessList通過socket接口通過lmkd從列表中移除司忱。這個列表就是lmkd監(jiān)控的進程列表,詳情參考LowMemoryKiller的實現(xiàn)
Android設備原始的adj閥值如圖:
Android進程分為了6個等級皇忿,對應不同內存閥值, 當lmkd監(jiān)控的系統(tǒng)內存小于某一個閥值的時候坦仍,就會開始查殺小于或等于對應優(yōu)先級的進程鳍烁,當釋放的內存達到最大閥值之后就停止查殺。
- 前臺進程 FOREGROUND_APP_ADJ (0)
前臺進程是指那些有組件正和用戶進行交互的應用程序的進程繁扎,也稱為Active進程幔荒。這些都是Android嘗試通過回收其他應用程序來使其保持相應的進程。這些進程的數(shù)量非常少梳玫,只有等到最后關頭才會終止這些進程爹梁,是用戶最不希望終止的進程。例如:而當你運行瀏覽器這類應用時提澎,它們的界面就會顯示在前臺姚垃,它們就屬于前臺進程,當你按home鍵回到主界面虱朵,他們就變成了后臺程序莉炉。
如果一個進程滿足以下任一條件钓账,即視為前臺進程:
(1)托管處于活動狀態(tài)的Activity,也就是說絮宁,它們位于前臺并對用戶事件進行響應梆暮,此時的情形為響應了Activity中的onResume()生命周期方法,但沒有響應onPause()绍昂。
(2)托管正在執(zhí)行onReceive()方法處理事件程序的BroadcastReceiver啦粹。
(3)托管正在執(zhí)行onStart()、onCreate()或onDestroy()事件處理程序的Service窘游。
(4)托管正在運行且被標記為在前臺運行的Service唠椭,即調用了該Service的startForeground()方法。
(5)托管某個Service忍饰,且該Service正綁定在用戶正在交互的Activity的Service贪嫂,即該Activity正處于活動狀態(tài)。
- 可見進程 VISIBLE_APP_ADJ (100)
沒有任何前臺組件艾蓝、但仍然會影響用戶在屏幕上所見內容的進程力崇。如果一個進程滿足以下任一條件,即視為可見進程:
(1)托管不在前臺赢织、但仍對用戶可見的Activity(已調用其onPause()方法)亮靴。例如:如果前臺Acitivty啟動了一個對話框,或者啟動了一個非全屏于置,亦或是一個透明的Activity茧吊,允許在其后顯示上一個Activity,則可能會發(fā)生這種情況八毯,這類Activity不在前臺運行搓侄,也不能對用戶事件作出反應。
(2)托管綁定到可見Activity的Service
可見進程被視為是極其重要的進程宪彩,這類進程的數(shù)量也很少休讳,只有在資源極度匱乏的環(huán)境下,為保證前臺進程繼續(xù)執(zhí)行時才會終止尿孔。
- 可感知進程 PERCEPTIBLE_APP_ADJ (200)
用戶可以感知到的進程俊柔,如后臺播放音樂的進程
- 備份進程 BACKUP_APP_ADJ (300)
處于備份過程中的進程
- CACHED_APP_MIN (900)
緩存進程ADJ最小值
- CACHED_APP_MAX (906)
緩存進程ADJ的最大值
lmkd根據以上6個等級對進程進程查殺,只要內存閥值達到某個等級活合,小于該優(yōu)先級等級的進程都屬于查殺對象雏婶。
當進程被LMK殺死后,binder死亡通知會通知AMS, AMS執(zhí)行handleAppDiedLocked來進程善后處理白指。清理該進程的信息留晚,并將該進程從AMS中移除。
LMK對進程的查殺是依賴adj等級的告嘲,但是進程adj如何進行分配的错维?這就要看下AMS對進程adj的管理策略奖地。
AMS對保存在內存中的進程管理策略
Android對進程管理是最大限度的將進程保留在內存中,在內存不夠的時候殺死一些不重要的進程赋焕,如何決定哪些進程是不重要的参歹,哪些進程是重要的,以及如何殺死進程隆判,這個就是Android進程管理的策略犬庇。
優(yōu)先級分類
ADJ取值 | 進程類別 | 含義 |
---|---|---|
-1000 | NATIVE_ADJ | native進程 |
-900 | SYSTEM_ADJ | System_server進程 |
-800 | PERSISTENT_PROC_ADJ | 系統(tǒng)persistent進程 |
-700 | PERSISTENT_SERVICE_ADJ | 系統(tǒng)或者persistent進程綁定的進程 |
0 | FOREGROUND_APP_ADJ | 前臺進程 :和用戶交互的進程,不到萬不得已不能殺死 |
100 | VISIBLE_APP_ADJ | 可見進程:該進程的某個UI組件是可以被用戶看見的侨嘀,但是沒有和用戶進行交互臭挽,不能隨便殺死,影響用戶體驗 |
200 | PERCEPTIBLE_APP_ADJ | 可感知進程:該進程的某個組件可以被用戶感知到咬腕,如后臺音樂播放 |
300 | BACKUP_APP_ADJ | 備份進程:不可輕易打斷欢峰,否則容易引起不可修復的數(shù)據錯誤 |
400 | HEAVY_WEIGHT_APP_ADJ | 重量級進程 |
500 | SERVICE_ADJ | 服務進程 |
600 | HOME_APP_ADJ | Lanucher進程 |
700 | PREVIOUS_APP_ADJ | 上一個訪問的進程 |
800 | SERVICE_B_ADJ | B list中的進程 |
900 | CACHED_APP_MIN_ADJ | 不可見進程adj最小值 |
906 | CACHED_APP_MAX_ADJ | 不可見進程adj最大值 |
1001 | UNKNOWN_ADJ | 錯誤的adj值 |
Android將進程的優(yōu)先級分為以上幾種,從表格中可以看出郎汪,值越小優(yōu)先級越高赤赊,小于0的優(yōu)先級的進程基本上不會被殺死的,這些都是系統(tǒng)的重要進程煞赢,殺死之后會影響到整個系統(tǒng)的運行。
進程的優(yōu)先級是如何分配的呢哄孤?
Android的ProcessRecord中有adj變量照筑,代表當前進程的優(yōu)先級,每當系統(tǒng)中的進程組件發(fā)生變化瘦陈,就會調整某個或者調整全部進程的adj優(yōu)先級凝危。
進程的LRU列表管理
在AMS服務中對于進程管理有一系列保存進程信息ProcessRecord的容器,其中mLruProcesses列表用于按照進程的最近的使用情況晨逝,對進程進行排序保存.
final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
AMS將mLruProcesses列表分成了三個區(qū)域蛾默,使用兩個變量來記錄三個區(qū)域的分割點
- mLruProcessServiceStart 表示從這個位置之后存放包含Service相關組件的進程信息
- mLruProcessActivityStart 表示從這個位置之后存放包含Activity相關組件的進程信息
LRU列表中進程位置調整主要遵循的策略如下:
- 如果當前正在和用戶交互的進程放在列表的尾部
- 如果包含Activity的進程,但是卻不是Top進程的捉貌,放到尾部倒數(shù)第二位置
- 如果只包含Service的進程支鸡,放到Service部分的尾部
- 如果其他進程,則放到其他進程部分的尾部
調整完當前進程的位置后趁窃,還需要調整當前進程所依賴進程的優(yōu)先級牧挣,比如依賴另一個進程的Service或者Provider,則不希望其進程被kill掉醒陆。調整依賴進程優(yōu)先級的邏輯總體如下:
- 如果其他進程的位置被自己進程位置靠后瀑构,說明自己依賴進程優(yōu)先級本身就是比自己進程高的,這時候不需要處理
- 如果依賴進程的是包含有Acitivity組件的刨摩,也不需要處理
- 如果依賴進程的位置比自己靠前寺晌,優(yōu)先級被自己低世吨,則要適當?shù)膶⑵湮恢猛笳{,因為越靠近LRU列表尾部的進程呻征,說明該進程剛剛運行過另假,重要性比較高.
根據以上一系列邏輯之后,進程在LRU列表中的位置就調整完了怕犁,但是边篮,不是要調整進程的優(yōu)先級adj么?調整LRU做什么呢奏甫?LRU Cache中存放的進程是根據最近使用過的來排順序的戈轿,越是最近使用的越是靠近列表尾部,當然分為三個區(qū)域的阵子。LRU列表調整完成之后思杯,adj調整的時候就會根據LRU列表尾部開始循環(huán)遍歷計算進程的adj值, 最近使用的進程adj值會被先計算。
并不是所有的進程都會被計算adj值挠进,保存到內存中色乾,在內存中保存到進程是有限制的,比如系統(tǒng)會限制Cache類型的進程和Empty類型的進程每種最多保存16個领突,也就是最多32個. 超過32個之后的進程會被直接殺死暖璧。也就是說LRU列表中靠近底部的很久未使用的進程很可能會被AMS直接殺死,根本不會保存到內存中君旦。
進程優(yōu)先級計算
- 優(yōu)先級 < 0的系統(tǒng)重要進程
- NATIVE_ADJ(-1000):是由init進程fork出來的Native進程澎办,并不受system管控;
- SYSTEM_ADJ(-900):是指system_server進程金砍;
- PERSISTENT_PROC_ADJ(-800): 是指在AndroidManifest.xml中申明android:persistent=”true”的系統(tǒng)(即帶有FLAG_SYSTEM標記)進程局蚀,persistent進程一般情況并不會被殺,即便被殺或者發(fā)生Crash系統(tǒng)會立即重新拉起該進程恕稠。
- PERSISTENT_SERVICE_ADJ(-700):是由startIsolatedProcess()方式啟動的進程琅绅,或者是由system_server或者persistent進程所綁定(并且?guī)в蠦IND_ABOVE_CLIENT或者BIND_IMPORTANT)的服務進程
以上進程都是系統(tǒng)重要進程,其adj優(yōu)先級是在啟動的時候就設置好鹅巍,無需重新計算其優(yōu)先級千扶。
- FOREGROUND_APP_ADJ (0) 前臺進程
1:滿足以下任一條件的進程都屬于FOREGROUND_APP_ADJ(0)優(yōu)先級:
正處于resumed狀態(tài)的Activity
正執(zhí)行一個生命周期回調的Service(比如執(zhí)行onCreate,onStartCommand,onDestroy等)
正執(zhí)行onReceive()的BroadcastReceiver
通過startInstrumentation()啟動的進程
場景2: 當客戶端進程activity里面調用bindService()方法時flags帶有BIND_ADJUST_WITH_ACTIVITY參數(shù),并且該activity處于可見狀態(tài)昆著,則當前服務進程也屬于前臺進程县貌,源碼如下:
場景3: 對于provider進程,還有以下兩個條件能成為前臺進程:
當Provider的客戶端進程ADJ<=FOREGROUND_APP_ADJ時凑懂,則Provider進程ADJ等于FOREGROUND_APP_ADJ
當Provider有外部(非框架)進程依賴煤痕,也就是調用了getContentProviderExternal()方法,則ADJ至少等于FOREGROUND_APP_ADJ
- VISIBLE_APP_ADJ (100) 可見進程
當ActivityRecord的visible=true,也就是Activity可見的進程摆碉。
可見進程VISIBLE_APP_ADJ(100)跟PERCEPTIBLE_APP_ADJ(200)可感知進程之間有99個槽塘匣,用于細化可見進程的adj值.
調用rankTaskLayersIfNeed對TaskRecord進行排序,按照TaskRecord對VisibleApp進行細化
- PERCEPTIBLE_APP_ADJ(200) 可感知進程
foregroundServices非空:前臺服務進程巷帝,執(zhí)行startForegroundService()方法
app.forcingToImportant非空:執(zhí)行setProcessImportant()方法忌卤,比如Toast彈出過程。
hasOverlayUi非空:非activity的UI位于屏幕最頂層楞泼,比如顯示類型TYPE_APPLICATION_OVERLAY的窗口
- BACKUP_APP_ADJ(300) 備份進程
執(zhí)行bindBackupAgent()過程驰徊,設置mBackupTarget值;
執(zhí)行clearPendingBackup()或unbindBackupAgent()過程堕阔,置空mBackupTarget值棍厂;
- HEAVY_WEIGHT_APP_ADJ(400) 重量級進程
realStartActivityLocked()過程,當應用的privateFlags標識PRIVATE_FLAG_CANT_SAVE_STATE超陆,設置mHeavyWeightProcess值牺弹;
finishHeavyWeightApp(), 置空mHeavyWeightProcess值
- SERVICE_ADJ(500) 服務進程
沒有啟動過Activity,并且30分鐘之內活躍過的服務進程
- HOME_APP_ADJ(600) Launcher進程
當類型為ACTIVITY_TYPE_HOME的應用啟動后會設置mHomeProcess时呀,比如桌面APP张漂。
- PREVIOUS_APP_ADJ(700) 上一個活動的進程
用戶上一個使用的包含UI的進程,為了給用戶在兩個APP之間更好的切換體驗谨娜,將上一個進程ADJ設置到PREVIOUS_APP_ADJ的檔次航攒。 當activityStoppedLocked()過程會更新上一個應用
當provider進程,上一次使用時間不超過20S的情況下瞧预,優(yōu)先級不低于PREVIOUS_APP_ADJ
- SERVICE_B_ADJ(800) B類服務進程
A類Service占比過高:當A類Service個數(shù) > Service總數(shù)的1/3時屎债,則加入到B類Service。
內存緊張&&A類Service占用內存較高:當系統(tǒng)內存緊張級別(mLastMemoryLevel)高于ADJ_MEM_FACTOR_NORMAL垢油,且該應用所占內存lastPss大于或等于CACHED_APP_MAX_ADJ級別所對應的內存閾值的1/3
以上內存參考 解讀Android進程優(yōu)先級ADJ算法, 該文章分析更加詳細
- Cached和Empty進程 (900 ~ 906)
Android默認對cached進程和Empty進程有最大數(shù)量限制為32個,Cached進程和Empty進程默認上限分別都是16個圆丹。
系統(tǒng)將900 ~ 906 這幾個adj的值分成6個卡槽滩愁,分別用來放緩存進程和空進程
分別計算當前共有多少個空進程和緩存進程,分別平均放到對應的卡槽中辫封,每個卡槽中可能會放多個進程. 每種類型的進程超過16個之后就直接kill掉硝枉,不再為其分配adj有l(wèi)mk監(jiān)控了.
進程優(yōu)先級設置
ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
其他調整
Android進程優(yōu)先級ADJ的每一個ADJ級別往往都有多種場景,使用adjType完美地區(qū)分相同ADJ下的不同場景倦微; 不同ADJ進程所對應的schedGroup不同妻味,從而分配的CPU資源也不同,schedGroup大體分為TOP(T)欣福、前臺(F)责球、后臺(B); ADJ跟AMS中的procState有著緊密的聯(lián)系。
adj:通過調整oom_score_adj來影響進程壽命(Lowmemorykiller殺進程策略)雏逾;
schedGroup:影響進程的CPU資源調度與分配嘉裤;
procState:從進程所包含的四大組件運行狀態(tài)來評估進程狀態(tài),影響framework的內存控制策略栖博。比如控制緩存進程和空進程個數(shù)上限依賴于procState屑宠,再比如控制APP執(zhí)行handleLowMemory()的觸發(fā)時機等。