Android Low Memory Killer 機制

LowMemoryKiller是Android 系統在Linux kernel的OOMKiller基礎上打的一個補丁。OOMKiller在kernel 無法再分配內存的時候,尋找一個得分最高的進程來殺掉。LowMemoryKiller則提早一步,經過把剩餘內存劃分紅不一樣的級別,內存在消耗的過程當中,觸發不一樣的級別,殺死相應的app進程。在觸發OOMKiller前,大量緩存的app進程已經被殺死掉了。java

先簡單說一下OOMKiller。android

咱們查看任一進程的proc信息(如:/proc/1), 都會看到如下三個參數:算法

  • oom_score -- 是該進程的最終得分,分數越高,越容易被殺死;
  • oom_score_adj -- 範圍:[-1000, 1000],是kernel用來配置進程優先級的。值越低,最終的oom_score越低。
  • oom_adj -- 範圍:[-16, 15],是給用戶來配置進程優先級的。爲了方便用戶配置,提供了範圍更小的oom_adj參數,數字越小優先級越高,-17表示該進程被保護,不被OOMKiller殺死。

所以,用戶設置oom_adj後,kernel會轉換並更新該進程實際的oom_score_adj值,它們的換算關係是:數組

#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;

從上面能夠看到,這個算法是有損的,因此,有時候會發現讀出的oom_adj的值同設置得不同了。如,設置oomajd=13, 那麼 oom_score_adj=764,再次讀oom_adj, 764 * 17 / 1000 == 12了。緩存

當內存耗盡的時候,OOMKiller會調用 out_of_memory()來select_bad_process(), oom_score最大的值就是那個將要被殺死的bad process。 oom_badness()以oom_score_adj做爲基礎值,根據是否爲管理員進程,佔用的內存狀況,來計算出最終的oom_score值,分值越高,越容易被殺死。app

OOMKiller相應的源碼文件:socket

/fs/proc/base.c
/mm/oom_kill.c

在Android系統裏面,當app的狀態發生變化,如:建立,收到廣播,喚醒,放入後臺等, ActivityManagerService的updateOomAdjLocked() 會computeOomAdjLocked(),而後,經過applyOomAdjLocked()把app的oom_adj值寫入到Linux Kernel。函數

那麼AN是如何計算每一個app最終的oom_adj值的呢?工具

咱們知道oom_adj的範圍是[-16, 15],AN根據app進程的特性進行了分類,不一樣的類別,對應不一樣的數值。網站

  • 9 ~ 15: 是緩存到後臺的app。
  • 8: service B 列表, 長時間未使用的service進程。
  • 7: 前一個app。
  • 6: Home app。
  • 5: 包含service的app 進程。
  • 4: 高權重的應用--隱藏的屬性,AN P以上版本才能配置: android:cantSaveState="true"
  • 3: backup app--正在被備份的app。
  • 2: 用戶可感知的後臺進程,如:後臺背景音樂播放器。
  • 1: 前臺app啓動的一些可見的組件,如: 彈出的Email activity。
  • 0: 前臺app.
  • -11: persistent service -- android:persistent:=true
  • -12: 常駐內存的系統app--如:系統自帶的撥號app。
  • -16: 系統進程--如:system_server進程。
  • -17: 不受oom_adj的管理,該進程不會被殺死,也native process的默認值。

LowMemoryKiller註冊了shrinker--Linux Kernel的一個內存管理工具,當kernel須要回收內存時,會回調LowMemoryKiller的lowmem_shrink(),它先檢查kernel 剩下多少內存,根據剩下的內存數量來匹配數組 lowmem_minfree[], 找到數組索引值,而後,再使用該索引值,從 lowmem_adj[]這個數組裏面就獲得目標oom_adj值,最終,在大於等於該目標oom_adj的進程中,殺死擁有最大oom_adj值的進程--send_sig(SIGKILL, selected, 0) 。算法其實很簡單,就是兩個一維數組的映射。

static short lowmem_adj[6] = {
    0,
    1,
    6,
    12,
};
static int lowmem_minfree[6] = {
    3 * 512, /* 6MB */
    2 * 1024, /* 8MB */
    4 * 1024, /* 16MB */
    16 * 1024, /* 64MB */
};

如: 系統剩下的內存爲 31024, 它 小於 4 1024,對應的數組索引是 2, lowmem_adj[2]對應的是 6,那麼系統將在oom_adj>=6的進程中,找一個最大的oom_adj的進程,而後,殺死它釋放內存。

Android在初始化的時候,會經過ProcessList::updateOomLevels()來設定上面兩個數組的初始值,咱們能夠經過framework的config.xml,或 /sys文件系統接口進行調整lowmem_minfree []。

/sys/module/lowmemorykiller/parameters/minfree

<integer name="config_lowMemoryKillerMinFreeKbytesAbsolute">-1</integer>
<integer name="config_lowMemoryKillerMinFreeKbytesAdjust">0</integer>

config_....KbytesAbsolute:非-1的狀況下,是絕對值, AN使用下面算法,獲得實際數組值。

for ( int i = 0 ; i < mOomAdj.length; i++) {
    mOomMinFree[ i ] = (int) ((float)minfree_abs * mOomMinFree[ i ] / mOomMinFree[ mOomAdj.length - 1 ] );
}

config_....KbytesAdjust: 非0狀況下, 直接在每一個數組值上 += reserve_adj;

如: <integer name="config_....KbytesAdjust">-512</integer>,代表每一個數組值都減小512。

lowmem_adj[] 能夠經過/sys 文件系統接口來進行調整。

/sys/module/lowmemorykiller/parameters/adj

固然了在實際上開發過程當中,也能夠直接在這個函數裏面打補丁,或者讀取系統屬性,經過屬性來進行配置等等。 像MStar方案,就定義了兩個屬性來進行第三方的配置: ro.mstar.lmkd.minfree和ro.mstar.lmkd.adj

到這裏,基本上LowMemoryKiller算是說完了,最後,簡單介紹下,AN是如何把oom_adj值傳給kernel的。

AN經過ProcessList:setOomAdj(),用socket與lmkd通信, lmkd經過/sys文件系統接口,把oom_adj值傳遞給LinuxKernel。

/sys/module/lowmemorykiller/parameters/minfree
/sys/module/lowmemorykiller/parameters/adj

相關的源碼文件分別在:

system/core/lmkd/ 生成 /system/bin/lmkd
drivers/staging/android/lowmemorykiller.c
framework/base/...../server/am/

歡迎你們來個人網站交流:般若程序蟬:prajna.top
qrcode_258.jpg

相關文章
相關標籤/搜索