邊實驗邊分析 - Android LowMemoryKiller 介紹
一、簡述
在Linux中,存在著一個OOM 终结者(Out Of Memory killer),他會在内存過低的情況下,殺掉你的進程,來釋放空閑的内存維持正常的運作。Android中也不一例外的存在者一個殺手,但是他不叫OOM殺手,而叫做LMK殺手(Android LowMemoryKiller),具體的原理不在這裏闡述,想知道的朋友可以自行Google一下,我們這裏只要知道這個殺手,他會在機器内存不足的情況下,進行殺掉進程的動作,而這個殺手不是隨便濫殺,而是有目標的,這個目標是由ActivityManagerService去指定並更新的,ActivityManagerService會將所有進程進行分類,並會調整每個進程的分類以及給一個分值來表示,該分值越高,越容易成爲殺手的目標而被幹掉,這個分值被保存在proc/{pid}/oom_score_adj下。
二、Process 分類
分類的具體定義值,我們可以在Android的com.android.server.am.ProcessList裏面查看到:
比如上圖中的FOREGROUND_APP_ADJ的值為0,則查看他的oom_score_adj的分值會是0,表示該APP當前在前臺可見狀態,再比如NATIVE_ADJ為-1000,則説明是Native的進程,複數可以認爲基本上不會被殺手給幹掉的進程,而超過900的進程,則會很容易安排給殺手了,這個進程的APP,會被歸類為Cache APP。
三、Android 7.0以下及7.0以上的區別
這裏需要注意一點需要説明一下,在Android 7.0上系統會直接根據ProcessList的定義寫入oom_score_adj值,這個值是100,200,-1000這樣的大值,這樣的好處是可以表示一個範圍,便於更加細化ADJ;而在7.0以下的系統,ProcessList的值對應的是oom_adj裏面的值,這個值是1,2,-17這樣的值,這個oom_score_adj值是通過了oom_adj計算得出的(大致的計算公式可以參考:oom_adj * 1000 / 17),所以如果你要在7.0以下的系統中查看oom_adj的值的話,建議查看proc/{id}/oom_adj的值,這個值會和ProcessList的值對的起來一些,方便查看。
四、ADJ查看方式
知道了上述的知識點后,下一步我們就可以實際的來查看一下該ADJ值了,
這邊我們可以通過dumpsys meminfo先來查看一下系統給我們APP進行的分類情況:
dumpsys meminfo 查看到所有的進程分類
而知道了這個分類信息后,可以利用cat proc/{id}/oom_adj查看對應的進程
cat proc/{pid}/oom_score_adj
可以看到com.oppo.launcher的進程ADJ值為0,説明他在前臺可見狀態(因爲我停留在了桌面,而launcher就是這個桌面程序),所以不會被系統幹掉的。
現在我打開任意一個APP,再次查看的時候,會發現該值已經變成了100。
五、總結
使用上述的方法,基本上能夠幫你對process的進程有了一個大概的瞭解了,至於想知道具體細節的朋友,可以自己去查看一些源碼知識,加深一些這方面的理解,我這邊給出一個我自己總結的源碼中能追蹤到Android Manager Service修改adj值的一個方法路綫作爲參考,請結合Android 9.0源碼進行查看:
set oom_adj流程:主要通過ActivityManagerService裏的updateOomAdjLocked(舊版本會調用applyOomAdjLocked)方法寫入ADJ,ProcessList裏面會傳調用ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj);
setOomAdj方法通過localSocket與底層system/memory/lmkd/lmkd.cpp ->ctrl_data_handler -> ctrl_command_handler方法進行處理command,(具體command的格式參考這個裏面https://jekton.github.io/2019/03/21/android9-lmk-lmkd/),ctrl_data_read方法讀取出來command值,
lmkd_pack_get_cmd讀取出來package信息包括adj。writefilestring方法寫入進/proc/%d/oom_score_adj中。
可以看到其實android是通過localSocket與底層進行的通訊,是不是對這一塊内容又瞭解了許多了呢?