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); } }