Android App 保活之 ADJ 演算法
Android開發中,令人頭疼的保活問題始終纏繞每一個開發者。如何保證自己的程序不被系統回收呢?首當其衝應該是保證自己程序的優先順序。
Android系統在執行時,如果遭遇到記憶體過低,為保證系統穩定與流暢,會回收一部分不常用的程序(當然很多三方rom如miui會在電量過低也會回收)。這個回收過程當然不是隨意回收,系統需要有一個判斷程序優先順序的指標,幫助系統判斷哪些資源是優先順序高需要保留,哪些資源優先順序比較低需要釋放該資源。
ADJ就是系統殺死程序的重要指標
本文從linux\Android程序優先順序:adj分數,淺析一下如何檢視adj以及各個adj分數背後所代表的含義。
1.ADJ 如何檢視
利用adb shell
1.ps | grep 包名//檢視當前app的程序號 2.cat /proc/程序號/oom_adj//檢視當前程序的adj值(早期android和linux使用,現已廢棄,但仍然有效) 3.cat /proc/程序號/oom_score_adj//這個是新版本的檢視adj的命令,adj有效值為-1000~1000
2.ADJ的值的各種含義
ADJ級別取值 含義 NATIVE_ADJ-1000native程序 SYSTEM_ADJ-900僅指system_server程序 PERSISTENT_PROC_ADJ-800系統persistent程序 PERSISTENT_SERVICE_ADJ-700關聯著系統或persistent程序 FOREGROUND_APP_ADJ0前臺程序 VISIBLE_APP_ADJ100可見程序 PERCEPTIBLE_APP_ADJ200可感知程序,比如後臺音樂播放 BACKUP_APP_ADJ300備份程序 HEAVY_WEIGHT_APP_ADJ400重量級程序 SERVICE_ADJ500服務程序(A list中的service) HOME_APP_ADJ600Home程序 PREVIOUS_APP_ADJ700上一個程序 SERVICE_B_ADJ800B List中的Service CACHED_APP_MIN_ADJ900不可見程序的adj最小值 CACHED_APP_MAX_ADJ906不可見程序的adj最大值
3.ADJ觸發順序
ADJ是一種演算法,用於系統判斷程序優先順序以觸發Linux的LMK(LowMemoryKill)機制。一般觸發時機(Linux下)是在系統低記憶體時,為了維護正在執行的程序,殺掉優先順序比較低(adj值比較高)的其他程序。
在Android中這一機制有所改動。在ActivityManagerService裡有具體的計算Adj值的原始碼。
程序剛啟動時ADJ等於INVALID_ADJ,當執行完attachApplication(),該該程序的curAdj和setAdj不相等,則會觸發執行setOomAdj()將該程序的節點/proc/pid/oom_score_adj寫入oomadj值。下圖引數為Android原生閾值,當系統剩餘空閒記憶體低於某閾值(比如147MB),則從ADJ大於或等於相應閾值(比如900)的程序中,選擇ADJ值最大的程序,如果存在多個ADJ相同的程序,則選擇記憶體最大的程序。 如下是64位機器,LMK預設閾值圖:
----------ADJ----------------Memory Left------------ FOREGROUND_APP_ADJ(0)73MB VISIBLE_APP_ADJ(100)92MB PERCEPTIBLE_APP_ADJ(200)110MB BACKUP_APP_ADJ(300)129MB CACHED_APP_MIN_ADJ(900)221MB CACHED_APP_MAX_ADJ(906)332MB
4.高階程序 ADJ<0的程序
1.NATIVE_ADJ(-1000):是由init程序fork出來的Native程序,並不受system管控;
2.SYSTEM_ADJ(-900):是指system_server程序;
3.PERSISTENT_PROC_ADJ(-800): 是指在AndroidManifest.xml中申明android:persistent=”true”的系統(即帶有FLAG_SYSTEM標記)程序,persistent程序一般情況並不會被殺,即便被殺或者發生Crash系統會立即重新拉起該程序。
4.PERSISTENT_SERVICE_ADJ(-700):是由startIsolatedProcess()方式啟動的程序,或者是由system_server或者persistent程序所繫結(並且帶有BIND_ABOVE_CLIENT或者BIND_IMPORTANT)的服務程序
5.總結
Android程序優先順序ADJ的每一個ADJ級別往往都有多種場景,使用adjType完美地區分相同ADJ下的不同場景; 不同ADJ程序所對應的schedGroup不同,從而分配的CPU資源也不同,schedGroup大體分為TOP(T)、前臺(F)、後臺(B); ADJ跟AMS中的procState有著緊密的聯絡。
1.adj:通過調整oom_score_adj來影響程序壽命(Lowmemorykiller殺程序策略);
2.schedGroup:影響程序的CPU資源排程與分配;
3.procState:從程序所包含的四大元件執行狀態來評估程序狀態,影響framework的記憶體控制策略。比如控制快取程序和空程序個數上限依賴於procState,再比如控制APP執行handleLowMemory()的觸發時機等。
為了說明整體關係,以ADJ為中心來講解跟adjType,schedGroup,procState的對應關係,下面以一幅圖來詮釋整個ADJ演算法的精髓,幾乎涵蓋了ADJ演算法調整的絕大多數場景。

image
6.最後,開發時應注意:
1.UI程序與Service程序一定要分離,因為對於包含activity的service程序,一旦進入後臺就成為”cch-started-ui-services”型別的cache程序(ADJ>=900),隨時可能會被系統回收;而分離後的Service程序服務屬於SERVICE_ADJ(500),被殺的可能性相對較小。尤其是系統允許自啟動的服務程序必須做UI分離,避免消耗系統較大記憶體。
只有真正需要使用者可感知的應用,才呼叫startForegroundService()方法來啟動前臺服務,此時ADJ=PERCEPTIBLE_APP_ADJ(200),常駐記憶體,並且會在通知欄常駐通知提醒使用者,比如音樂播放,地圖導航。切勿為了常駐而濫用前臺服務,這會嚴重影響使用者體驗。
2.程序中的Service工作完成後,務必主動呼叫stopService或stopSelf來停止服務,避免佔據記憶體,浪費系統資源;
3.不要長時間繫結其他程序的service或者provider,每次使用完成後應立刻釋放,避免其他程序常駐於記憶體;
4.APP應該實現介面onTrimMemory()和onLowMemory(),根據TrimLevel適當地將非必須記憶體在回撥方法中加以釋放。當系統記憶體緊張時會回撥該介面,減少系統卡頓與殺程序頻次。
5.減少在保活上花心思,更應該在優化記憶體上下功夫,因為在相同ADJ級別的情況下,系統會選擇優先殺記憶體佔用的程序。
原文: https://blog.csdn.net/zhangbijun1230/article/details/81347749