Low memory killer是安卓內(nèi)存管理的一種策略,其主要目的在于當系統(tǒng)內(nèi)存不足時按照進程的重要性程度殺掉一些進程來保證系統(tǒng)有足夠內(nèi)存。
Low memory killer
Low memory killer機制實現(xiàn)是在kernel里面,具體實現(xiàn)可參看:kernel/drivers/staging/android/lowmemorykiller.c,。其機制也比較簡單,主要分為兩個兩面:
1)什么時候運行;
2)運行時根據(jù)什么來決定殺死哪個進程。
先看第一點,當你看到lowmemorykiller源碼,會看到 initcall的如下操作:
static int __init lowmem_init(void)
{
? ? register_shrinker(&lowmem_shrinker);
? ? return 0;
}
相關(guān)lowmem_shrinker數(shù)據(jù)結(jié)構(gòu)如下:
static struct shrinker lowmem_shrinker = {
? ? .scan_objects = lowmem_scan,
? ? .count_objects = lowmem_count,
? ? .seeks = DEFAULT_SEEKS * 16
};
從代碼可以看到,真正執(zhí)行掃描并殺進程的是lowmem_scan這個函數(shù)。那么是誰來調(diào)用的呢?當當當。。。這個大功臣就是kswapd。kswapd是一個內(nèi)核線程,它會在回收內(nèi)存分頁時遍歷shrinker鏈表并執(zhí)行回調(diào),對于lowmemorykiller而言,也就是注冊進去的lowmen_scan。
第一個問題解決了,那么我們看下第二個問題,如何決定殺死哪個進程?我們可以看到在lowmemorykiller.c中存在兩個閾值表:
static int lowmem_adj[6] = {?
?????? 0,
?????? 1,
?????? 6,
?????? 12,
};
static int lowmem_minfree[6] = {
?????? 3 * 512, /*6MB */
?????? 2 * 1024,????? /*8MB */
?????? 4 * 1024,????? /*16MB */
?????? 16 * 1024,??? /* 64MB */
};
對于這兩張表,lowmem_adj代表警戒的級數(shù),lowmem_minfree代表對應級數(shù)的內(nèi)存。對于安卓而言,實際并不會使用這個默認表。而是會通過module_param_named將這兩張表開放給文件系統(tǒng)的/sys/module/lowmemorykiller/parameters目錄,adj對應于lowmem_adj,minfree對應與lowmem_minfree。而AMS會根據(jù)系統(tǒng)的屏幕分辨率以及物理內(nèi)存去更新這兩個表對應的內(nèi)容,詳細參考ProcessList的applyDisplaySize()方法以及后續(xù)調(diào)用的updateOomLevels()。
從系統(tǒng)應用角度而言,主要層級對應如下:

系統(tǒng)會根據(jù)minfree中對應的值去殺掉對應等級的進程。如果不想App被殺掉,系統(tǒng)應用可以考慮添加android:persistent屬性,或者在lowmemorykiller里面添加白名單處理。
native lmkd
安卓L之后,提供了一種用戶空間native層面的lmkd方案。該機制不同于內(nèi)核lowmemorykiller的觸發(fā)機制,而是通過vmpressure來觸發(fā)。
首先看所需的配置:
內(nèi)核方面需要使能CONFIG_MEMCG=y以及CONFIG_MEMCG_SWAP=y來使能vmpressure功能,如果想使能memcg更多功能,還可以使能CONFIG_MEMCG_SWAP_ENABLED=y和CONFIG_MEMCG_KMEM=y來限制swap空間以及內(nèi)存空間等資源的大小。
用戶空間方面,如果要使能lmkd還需要配置ro.config.low_ram=true。另外還有其他一些配置,可參考lmkd.c main中的配置項。
這兩點滿足了,那么由于在init.rc中已經(jīng)配置了lmkd進程的啟動,在lmkd初始化過程中,就會根據(jù)是否是能了memcfg以及ro.config.low_ram決定是否使能給予vmpressure的lmkd。
lmkd會注冊兩種類型的vmpressure事件監(jiān)聽。一種是medium類型的內(nèi)存壓測,另一種是critical類型的內(nèi)存壓測,這兩種壓力的門限在內(nèi)核中分別是60%和95%。計算公式如下:pressure = (1-reclaimed/scanned)100%, 也就是說pressure等于不可回收的內(nèi)存所占的總內(nèi)存的比例。一旦到達了這個門限,那么用戶空間的epoll調(diào)用就會監(jiān)聽到相應的事件從而選擇殺進程操作。
與之相關(guān)的還有幾個配置,包括:
ro.lmk.medium : 中等內(nèi)存壓力時可殺的app對應的最小adj, 默認800
ro.lmk.critical: critical壓力下可殺的app對應的最小adj,最小是0
ro.lmk.upgrade_pressure: medium內(nèi)存壓力時,需要升級為critcal對應的內(nèi)存swappiness比例,默認50
ro.lmk.downgrade_pressure: critical內(nèi)存壓力時,需要降級為medium對應的內(nèi)存swappiness比例,默認60.
另外,lmkd還有一個localsocket和AMS通信,用于以下幾個方面用途:
1)更新lowmemorykiller中adj和內(nèi)存的門限。
2)更新pid的list中對應的adj。
3)移除已經(jīng)被殺掉的app進程。
總結(jié)
總結(jié)這兩種實現(xiàn)方式,內(nèi)核空間的lowmemorykiller控制更為精細,但是在交互和控制上不如lmkd。lmkd的優(yōu)勢在于控制上更為方便,至于說回收速度等方面的優(yōu)勢,不好評判,也許是lmkd實現(xiàn)者的自我說法。有關(guān)lmkd的演進可參考:https://blog.csdn.net/alien75/article/details/53322769?utm_source=blogxgwz0。