Linux的內存的使用原則就是不要浪費內存,因此在程序退出時在一段時間內還停留在內存中,這也是咱們下一次打開程序時發現快一些的緣由。可是這樣帶來的壞處就是若是駐留在內存中的程序多了,容易致使OOM(out of memory)的可能。Linux中使用內存監控機制來避免OOM發生。java
Linux本來存在一個內存監控機制OOM Killer,一旦發現內存使用進入一個臨界值就會自動按照必定的策略來清理。它的核心思想是,android
具體的作法是OOM Killer會根據一些參考因素,例如進程消耗內存,運行時間,OOM權重等指標計算出一個oom_score分數,這個分數越低,進程被殺死的機率越小,被殺死的時間越晚。數組
在Android中存在另外一個內存監控機制Low memory killer(LMK)。它實現一個不一樣級別的killer,根據進程的oom_adj 來殺死進程,釋放內存。oom_adj的大小和進程的類型以及進程被調度的次序有關,這個值越小,程序越重要,被殺的可能性越低。其源碼位於,kernel/drivers/staging/android/LowMemoryKiller.c。該文件中定義了兩個數組,用來調整killer行爲。app
static short lowmem_adj[6] = { 0, 1, 6, 12, }; static int lowmem_adj_size = 4; /*static*/ int lowmem_minfree[6] = { 3 * 512, /* 6MB */ 2 * 1024, /* 8MB */ 4 * 1024, /* 16MB */ 16 * 1024, /* 64MB */ };
上面定義的兩個數組時一一對應的,其中lowmem_adj表示的是被處理某一個級別的adj的值,lowmem_minfree則表示該級別對應的內存閾值。好比說adj=0的級別,它對應的內存閾值是6M,也就是在可用內存小於6M時,會清除adj大於等於0的全部進程。因此能夠看出adj越小,它被殺死的可能越小。less
Kernel代碼中設定了lowmem_adj和lowmem_minfree的默認值,能夠經過設置下面的文件來修改這兩組值。須要注意的是最多隻能設置6個級別,而且minfree的單位是page。ide
例如能夠在init.rc中修改其默認值,函數
write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15 write /proc/sys/vm/overcommit_memory 1 write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144
可是在Android運行時,AMS還會經過updateOomLevels()對LMK的參數進行調整。ui
LMK使用了kernel中的shrinker機制,在驅動加載時,向系統註冊了一個shrinker。當系統空閒內存頁面不足時就會調用該函數。LMK的shrinker實現以下,this
kernel\drivers\staging\android\lowmemorykiller.c static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc) { struct task_struct *tsk; struct task_struct *selected = NULL; int rem = 0; int tasksize; int i; short min_score_adj = OOM_SCORE_ADJ_MAX + 1; int minfree = 0; int selected_tasksize = 0; short selected_oom_score_adj; int array_size = ARRAY_SIZE(lowmem_adj); int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages; int other_file = global_page_state(NR_FILE_PAGES) - global_page_state(NR_SHMEM); if (lowmem_adj_size < array_size) array_size = lowmem_adj_size; if (lowmem_minfree_size < array_size) array_size = lowmem_minfree_size; for (i = 0; i < array_size; i++) { //依次遍歷策略閥值數組,從小到大,根據當前memory free狀況,取觸發adj值 minfree = lowmem_minfree[i]; if (other_free < minfree && other_file < minfree) { min_score_adj = lowmem_adj[i]; break; } } //這裏獲得的min_score_adj 就是此時內存狀態下 將會kill掉的最小score_adj ...... for_each_process(tsk) { ...... tasksize = get_mm_rss(p->mm); ...... if (selected) { if (oom_score_adj < selected_oom_score_adj) continue; if (oom_score_adj == selected_oom_score_adj && tasksize <= selected_tasksize) continue; }//能夠看到 遍歷一圈process 只爲找到一個 oom_score_adj tasksize 最大的process selected = p; selected_tasksize = tasksize; selected_oom_score_adj = oom_score_adj; } if (selected) { lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \ " to free %ldkB on behalf of '%s' (%d) because\n" \ " cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \ " Free memory is %ldkB above reserved\n", selected->comm, selected->pid, selected_oom_score_adj, selected_tasksize * (long)(PAGE_SIZE / 1024), current->comm, current->pid, other_file * (long)(PAGE_SIZE / 1024), minfree * (long)(PAGE_SIZE / 1024), min_score_adj, other_free * (long)(PAGE_SIZE / 1024)); trace_lowmem_kill(selected, other_file, minfree, min_score_adj, other_free); lowmem_deathpending_timeout = jiffies + HZ; send_sig(SIGKILL, selected, 0); //發送kill signal 去kill selected的process set_tsk_thread_flag(selected, TIF_MEMDIE); rem -= selected_tasksize; } }
lowmem_shrink()中,首先根據當前的內存狀態找到一個合適的ADJ值,再根據該ADJ找到tasksize最大的進程將其殺死。在判斷內存狀態時須要注意一下,與minfree作比較的是free和cache,也就是說只有當free和cache都小於minfree時才知足ADJ的條件。rest
LMK的策略是經過驅動來執行的,但其策略的參數是在應用層設定。參數的默認值能夠經過上文講到的配置文件來修改,在Android中參數是由AMS設置的。AMS中定義了ADJ和minfree相關的數組資源。
frameworks/base/services/core/java/com/android/server/am/ProcessList.java // These are the various interesting memory levels that we will give to // the OOM killer. Note that the OOM killer only supports 6 slots, so we // can't give it a different value for every possible kind of process. private final int[] mOomAdj = new int[] { FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ, BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ }; // These are the low-end OOM level limits. This is appropriate for an // HVGA or smaller phone with less than 512MB. Values are in KB. private final int[] mOomMinFreeLow = new int[] { 12288, 18432, 24576, 36864, 43008, 49152 }; // These are the high-end OOM level limits. This is appropriate for a // 1280x800 or larger screen with around 1GB RAM. Values are in KB. private final int[] mOomMinFreeHigh = new int[] { 73728, 92160, 110592, 129024, 147456, 184320 }; // The actual OOM killer memory levels we are using. private final int[] mOomMinFree = new int[mOomAdj.length];
策略參數的更新是由updateOomLevels()完成的。最終計算出來的minfree會與mOomMinFreeLow,mOomMinFreeHigh,minfree_adj,minfree_abs等多個參數相關。
frameworks/base/services/core/java/com/android/server/am/ProcessList.java private void updateOomLevels(int displayWidth, int displayHeight, boolean write) { …… int minfree_adj = Resources.getSystem().getInteger( com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust); int minfree_abs = Resources.getSystem().getInteger( com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute); …… for (int i=0; i<mOomAdj.length; i++) { int low = mOomMinFreeLow[i]; int high = mOomMinFreeHigh[i]; if (is64bit) { // Increase the high min-free levels for cached processes for 64-bit if (i == 4) high = (high*3)/2; else if (i == 5) high = (high*7)/4; } mOomMinFree[i] = (int)(low + ((high-low)*scale)); } if (minfree_abs >= 0) { for (int i=0; i<mOomAdj.length; i++) { mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i] / mOomMinFree[mOomAdj.length - 1]); } } if (minfree_adj != 0) { for (int i=0; i<mOomAdj.length; i++) { mOomMinFree[i] += (int)((float)minfree_adj * mOomMinFree[i] / mOomMinFree[mOomAdj.length - 1]); if (mOomMinFree[i] < 0) { mOomMinFree[i] = 0; } } } …… }
Android用用在各類activity生命週期切換時,會觸發AMS中的回收機制。在AMS的回收過程當中,還會去維護一個ADJ變量,做爲LMK行爲的參考依據。AMS回收機制的入口爲trimApplications(),它在不少地方都有調用。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java final void trimApplications() { synchronized (this) { int i; // First remove any unused application processes whose package // has been removed. for (i=mRemovedProcesses.size()-1; i>=0; i--) { final ProcessRecord app = mRemovedProcesses.get(i); if (app.activities.size() == 0 && app.curReceiver == null && app.services.size() == 0) { Slog.i( TAG, "Exiting empty application process " + app.processName + " (" + (app.thread != null ? app.thread.asBinder() : null) + ")\n"); if (app.pid > 0 && app.pid != MY_PID) { app.kill("empty", false); } else { try { app.thread.scheduleExit(); } catch (Exception e) { // Ignore exceptions. } } cleanUpApplicationRecordLocked(app, false, true, -1); mRemovedProcesses.remove(i); if (app.persistent) { addAppLocked(app.info, false, null /* ABI override */); } } } // Now update the oom adj for all processes. updateOomAdjLocked(); } }
mRemovedProcesses 列表中主要包含了 crash 的進程、5 秒內沒有響應並被用戶選在強制關閉的進程、以及應用開發這調用 killBackgroundProcess 想要殺死的進程。調用 Process.killProcess 將全部此類進程所有殺死。updateOomAdjLocked()計算更新全部process的oomadj。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java final void updateOomAdjLocked() { ...... // First update the OOM adjustment for each of the // application processes based on their current state. int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ; int nextCachedAdj = curCachedAdj+1; int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ; int nextEmptyAdj = curEmptyAdj+2; for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); if (!app.killedByAm && app.thread != null) { app.procStateChanged = false; computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); ...... applyOomAdjLocked(app, TOP_APP, true, now); ...... }
computeOomAdjLocked()計算當前process的ADJ。ADJ值爲-17~15,越小優先級越高。AMS中根據進程的類型定義了ADJ的值,他們的意義分別以下:
ADJ | Description |
---|---|
CACHED_APP_MAX_ADJ = 15 | 當前只運行了不可見的Activity組件的進程 |
CACHED_APP_MIN_ADJ = 9 | |
SERVICE_B_ADJ = 8 | B list of Service。和A list相比,他們對用戶的黏合度要小些 |
PREVIOUS_APP_ADJ = 7 | 用戶前一次交互的進程。按照用戶的使用習慣,人們常常會在幾個經常使用的進程間切換,因此這類進程獲得再次運行的機率比較大 |
HOME_APP_ADJ = 6 | Launcher進程,他對用戶的重要性不言而喻 |
SERVICE_ADJ = 5 | 當前運行了application service的進程 |
EAVY_WEIGHT_APP_ADJ = 4 | 重量級應用程序進程 |
BACKUP_APP_ADJ = 3 | 用於承載backup相關操做的進程 |
PERCEPTIBLE_APP_ADJ = 2 | 這類進程用戶感受到但看不見,如後臺運行的音樂播放器 |
VISIBLE_APP_ADJ = 1 | 前臺可見的Activity進程,若是輕易殺死這類進程將嚴重影響用戶體驗 |
FOREGROUND_APP_ADJ = 0 | 當前正在前臺運行的進程,也就是用戶正在交互的那個程序 |
PERSISTENT_SERVICE_ADJ = -11 | 與系統進程或Persistent進程綁定的進程,說明該進程很 |
PERSISTENT_PROC_ADJ = -12 | Persistent性質的進程,如telephony |
SYSTEM_ADJ = -16 | 系統進程 |
這些是系統提供的adj,咱們還能夠改變本身進程的adj值,有如下兩種方式:
寫/proc/pid/oom_adj 值,在init.rc文件中就常常看到下面的語句。它設置進程的adj 值爲-16,屬於系統進程永遠不會被殺死。
on early-init
write /proc/1/oom_adj -16
繼續updateOomAdjLocked()流程。經過computeOomAdjLocked()獲得ADJ值後,applyOomAdjLocked()將其通過必定的修整,設置到對應的process。大致流程以下,
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java private final boolean applyOomAdjLocked(ProcessRecord app, ProcessRecord TOP_APP, boolean doingAll, long now) { ...... if (app.curAdj != app.setAdj) { ProcessList.setOomAdj(app.pid, app.info.uid, app.curAdj); if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v( TAG, "Set " + app.pid + " " + app.processName + " adj " + app.curAdj + ": " + app.adjType); app.setAdj = app.curAdj; } ...... }
setOomAdj()經過lmkd將ADJ值寫入到proc文件系統對應的節點上,其路徑爲"/proc/<pid>/oom_score_adj"。
frameworks/base/services/core/java/com/android/server/am/ProcessList.java /** * Set the out-of-memory badness adjustment for a process. * * @param pid The process identifier to set. * @param uid The uid of the app * @param amt Adjustment value -- lmkd allows -16 to +15. * * {@hide} */ public static final void setOomAdj(int pid, int uid, int amt) { if (amt == UNKNOWN_ADJ) return; long start = SystemClock.elapsedRealtime(); ByteBuffer buf = ByteBuffer.allocate(4 * 4); buf.putInt(LMK_PROCPRIO); buf.putInt(pid); buf.putInt(uid); buf.putInt(amt); writeLmkd(buf); long now = SystemClock.elapsedRealtime(); if ((now-start) > 250) { Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid + " = " + amt); } }