在AMS的内部属性中使用mLruProcesses集合保存所有的进程信息,AMS将所有进程按照优先级从低到高的顺序保存着对应的ProcessRecord信息,即排在前面的进程优先级越低,当系统内存不足时优先被Killer杀死;
在AMS的工作过程中,会不断调用updateLruProcessLocked()方法进行进程排序,在mLruProcesses()中保存按照以下规则保存数据 在AMS中越重要的进程会放在mLruProcesses集合的最后面,正在使用交互的进程会保存在最后,然后按照其他进程、Service进程、Activity进程的顺序重要行逐步增加,Activity进程的重要性最高,为了区分各个进程的位置,AMS中引入mLruProcessServiceStart和mLruProcessActivityStart变量保存二者的起始位置,在添加时当进程中包含活动则添加到mLruProcessActivityStart之后,如果不包含活动但包含Service服务,则添加到mLruProcessServiceStart之后;
updateLruProcessLocked源码 final void updateLruProcessLocked(ProcessRecord app, boolean activityChange, ProcessRecord client) { final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities || app.treatLikeActivity || app.recentTasks.size() > 0;//1、 final boolean hasService = false; if (!activityChange && hasActivity) { // 如果activityChange为false则无需改变位置 return; } mLruSeq++; // 更新编号 final long now = SystemClock.uptimeMillis(); app.lastActivityTime = now; // 保存更新的时间 if (hasActivity) { // 如果包含Activity, final int N = mLruProcesses.size(); if (N > 0 && mLruProcesses.get(N-1) == app) { return; } } else { if (mLruProcessServiceStart > 0 // 不包含Activit但已经存在其他段的最末尾,也无需更新 && mLruProcesses.get(mLruProcessServiceStart-1) == app) { return; } } int lrui = mLruProcesses.lastIndexOf(app); // 获取目前位置的index if (app.persistent && lrui >= 0) { return; } if (lrui >= 0) { if (lrui < mLruProcessActivityStart) { // 如果index小于mLruProcessActivityStart,则Activity段前移一位 mLruProcessActivityStart--; } if (lrui < mLruProcessServiceStart) {//如果index小于mLruProcessServiceStart,则Activity段前移一位 mLruProcessServiceStart--; } mLruProcesses.remove(lrui); //移除原来的app信息 } int nextIndex; // if (hasActivity) { final int N = mLruProcesses.size(); if ((app.activities.size() == 0 || app.recentTasks.size() > 0) && mLruProcessActivityStart < (N - 1)) { mLruProcesses.add(N - 1, app); // 如果进程不存在活动,但recentTasks存在任务则添加到最后位置 final int uid = app.info.uid; for (int i = N - 2; i > mLruProcessActivityStart; i--) { ProcessRecord subProc = mLruProcesses.get(i); if (subProc.info.uid == uid) { // 遍历集合中进程,匹配uid相等的进程 if (mLruProcesses.get(i - 1).info.uid != uid) {//处理其他进程,将该用户进程前移,其他进程后移 ProcessRecord tmp = mLruProcesses.get(i); mLruProcesses.set(i, mLruProcesses.get(i - 1)); mLruProcesses.set(i - 1, tmp); i--; } } else { break; } } } else { mLruProcesses.add(app); // 直接添加进程信息 } nextIndex = mLruProcessServiceStart; } else if (hasService) { 存在Service服务 mLruProcesses.add(mLruProcessActivityStart, app); // 保存在Service服务的开始位置 nextIndex = mLruProcessServiceStart; mLruProcessActivityStart++; // mLruProcessActivityStart后移 } else { int index = mLruProcessServiceStart; if (client != null) { int clientIndex = mLruProcesses.lastIndexOf(client);//获取当前客户端最高级的位置 if (clientIndex <= lrui) { clientIndex = lrui; // 如果最高级小则赋值最高级为lrui } if (clientIndex >= 0 && index > clientIndex) { index = clientIndex; } } mLruProcesses.add(index, app); // 保存在头部位置 nextIndex = index-1; mLruProcessActivityStart++; mLruProcessServiceStart++; } for (int j=app.connections.size()-1; j>=0; j--) { ConnectionRecord cr = app.connections.valueAt(j); if (cr.binding != null && !cr.serviceDead && cr.binding.service != null && cr.binding.service.app != null && cr.binding.service.app.lruSeq != mLruSeq && !cr.binding.service.app.persistent) { nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex, "service connection", cr, app); } } for (int j=app.conProviders.size()-1; j>=0; j--) { ContentProviderRecord cpr = app.conProviders.get(j).provider; if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) { nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex, "provider reference", cpr, app); } } }在updateLruProcessLocked()中主要执行流程:
判断进程中是否包含活动,如果activityChange为false则无需改变位置如果当前进程存在活动信息,则判断当前进程是不是在集合的最后一位,如果是则无需变换位置从集合中取出进程本身的位置index,判断mLruProcessActivityStart和mLruProcessServiceStart在移除进程后是否需要前移,并从集合中移除进程对象如果进程中包含Activity,则直接添加进程到集合最后,但如果不包含活动只是recentTasks存在执行的任务则替换为集合的最后一个,然后遍历集合,这里遍历的目的是为了集合进程的公平性,在一个用户的进程被插在最后时,该用户的其他进程需要前移,其他用户的进程需要后移;对于Service服务则直接保存在服务段的首位,然后后移mLruProcessActivityStart位置对于其他进程则保存在头部位置,但保存的原则就是不能超过其客户端为优先级最后调用updateLruProcessInternalLocked()更新绑定服务进程和包含ContentProvider的进程信息关于AMS中进程的管理就分析结束了,简单来说就是AMS会自动更新集合中进程 的位置,从而区分进程的区域和优先级部分,下面一起看下Android 中的进程管理机制;
Android的内存管理实际是通过与Linux协作完成的,Linux通过OOM KIller机制通知Android端执行内存释放操作,关于OOM机制主要包含以下特点:
在Android中运行一个叫OOM进程,该进程启动后首先会向Linux内核中注册成为OOM Killer当内核内存管理模块检测到内存低时会通知OOM进程,然后OOM Killer执行内存释放Android在启动一个新的进程时,AMS会把该进程的oom_adj值告诉 OOM Killer,oom_adj值表示进程的优先级(Android中 0 ~ 15)值越低则越重要,所谓的前台进程、后台进程、空进程最后都会反应在oom_adj值上;OOM Killer收到内存释放的消息后,按照预先设置的优先级开始强制退出低优先级的进程在AMS执行过程中会不断的更新进程的优先级,主要体现是根据进程条件设置进程中对应的adj信息,然后将赋值的adj更新到OOM机制中改变进程的等级信息,Android中区分进程的原则
前台进程 :ForeGround进程表示当前用户正在交互的进程,需要满足以下条件 具备正在可交互的前台Activity包含和前台交互Activity绑定的Service服务含有一个正在运行的前台服务,即Service中调用了startForGround()Service正在执行系列的生命周期(onxxx())方法Receiver执行onReceive() 可视进程:Visiable进程表示当前对用户可见的进程,满足以下条件 具备可视的Activit,这里的可视指的是调用了onPause()之后处于可见但不交互的状态具备服务于可视Activity的Service服务 服务进程 具备正常调用startService()启动运行的Service服务,但不具备上面两个条件,没有和前台或可视的Activity交互,只是服务自己在执行数据处理 后台进程 不满足上面的三个进程条件,但具备与用户不可见的Activity,即执行过onStop()之后的活动界面 后台空进程:没有任何运行组件的进程在前面的ActivityManagerService的内存管理分析知道,程序在activityIdleInternalLocked()的最后调用了trimApplications()执行进程的管理
final void trimApplicationsLocked() { for (i=mRemovedProcesses.size()-1; i>=0; i--) { // 1、删除mRemoveProcess进程中的数据 12210 final ProcessRecord app = mRemovedProcesses.get(i); // 获取进程app 12211 if (app.activities.size() == 0&& app.curReceiver == null && app.services.size() == 0) {//确定进程为空 12218 if (app.pid > 0 && app.pid != MY_PID) { 12219 app.kill("empty", false); // 直接杀死进程 12220 } else { 12222 app.thread.scheduleExit(); // 执行进程退出 12226 } 12227 cleanUpApplicationRecordLocked(app, false, -1); 12228 mRemovedProcesses.remove(i); // 移除进程信息 12235 } 12236 } updateOomAdjLocked() //2、为所有的进程更新 oom adj 的值 }trimApplications()中直接调用 trimApplicationsLocked()方法,执行删除mRemovedProcesses集合中保存的进程,mRemovedProcesses主要保存即将被清除的进程,一般mRemoveProcess集合中数据来源:
当进程被crash后会将该进程添加到集合中;当程序出现ANR时且用户点击关闭程序时,将进程添加到集合中;程序调用AMS.killBackgroundProcess()时,系统并不会直接杀死进程而是会将进程先添加到集合中;程序遍历mRemovedProcesses集合中的进程信息,然后判断当前进程是否为空进程,然后分别执行进程杀死和进程退出操作,最后从集合中清除进程信息,在清除mRemovedProcesses中进程后,调用updateOomAdjLocked()判断系统是否支持OOM Killer,并更新所有进程的优先级和管理进程,在查看源码之前先介绍下ProcessRecord类
ProcessRecord内部属性与OOM相关的变量 curAdj:进程当前的adj值,反应了进程的优先级 setAdj:上一个设置的adj的值 curRawAdj:第一次对app评级后计算的adj的值, setRawAdj:删一个curRawAdj设置的值 maxAdj:OOM的最大调整值updateOomAdjLocked()第二部分,主要计算每个进程的优先级,即oom_adj的值,如果底层Linux包含OOM Killer返回true,否则返回false;
final void updateOomAdjLocked() { final ActivityRecord TOP_ACT = resumedAppLocked(); // 1、获取最顶部的resume的ActivityRecord对象 final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;// 2、获取顶部活动所在的进程 final int N = mLruProcesses.size(); for (int i=mActiveUids.size()-1; i>=0; i--) { final UidRecord uidRec = mActiveUids.valueAt(i); uidRec.reset(); // 复位所有的UidRecord对象,UidRecord主要保存每个用户的最高优先级进程 } final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES; //获取允许空进程的数量,此处返回16 final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES - emptyProcessLimit; //获取允许的后台进程数16 //为后台进程、空进程开辟位置,此处计算结果为(906-900+1)/2 = 3 int numSlots = (ProcessList.CACHED_APP_MAX_ADJ // - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2; int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs; // 计算当前系统中空进程的数量 int emptyFactor = numEmptyProcs/numSlots;// 计算每个slot中保存的进程数量 int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ; // 后台进程的adj= 900 int nextCachedAdj = curCachedAdj+1; // 下一个位置+1 int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ; // 空进程adj=900 int nextEmptyAdj = curEmptyAdj+2; // 下一个位置+2 for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); // 遍历所有的进程ProcessRecord ,计算oom_adj的值 if (!app.killedByAm && app.thread != null) { computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); if (app.curAdj >= ProcessList.UNKNOWN_ADJ) { switch (app.curProcState) { case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: case ActivityManager.PROCESS_STATE_CACHED_RECENT: app.curRawAdj = curCachedAdj; //赋值后台进程 app.curAdj = app.modifyRawOomAdj(curCachedAdj); if (curCachedAdj != nextCachedAdj) { stepCached++; // 累计当前adj值中保存的进程数 if (stepCached >= cachedFactor) { // 超过每个slot数量后使用下一个next slot位置 stepCached = 0; curCachedAdj = nextCachedAdj; nextCachedAdj += 2; if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) { nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; } } } break; default: app.curRawAdj = curEmptyAdj; // 赋值空进程 app.curAdj = app.modifyRawOomAdj(curEmptyAdj); break; } } } } } for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); if (!app.killedByAm && app.thread != null) { applyOomAdjLocked(app, true, now, nowElapsed); // 更新进程的adj值 switch (app.curProcState) { case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: mNumCachedHiddenProcs++; numCached++; // 计算后台进程 if (numCached > cachedProcessLimit) { // 如果后台进程超过上限则杀掉进程 app.kill("cached #" + numCached, true); } break; case ActivityManager.PROCESS_STATE_CACHED_EMPTY: //系统内部会定义一个允许保留空进程的数量且不需要执行回收的,当超过此数量则执行杀死 if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES && app.lastActivityTime < oldTime) {//并且当前进程空闲时间超过设置要求的时间30min app.kill("empty for " + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime) / 1000) + "s", true); } else { numEmpty++; if (numEmpty > emptyProcessLimit) {// 空进程超过限制杀掉进程,此处设置为16 app.kill("empty #" + numEmpty, true); } } break; default: mNumNonCachedProcs++; break; } //对于单独进程的空进程,直接杀死进程 if (app.isolated && app.services.size() <= 0 && app.isolatedEntryPoint == null) { app.kill("isolated not needed", true); } else { final UidRecord uidRec = app.uidRecord; if (uidRec != null) { uidRec.ephemeral = app.info.isInstantApp(); if (uidRec.curProcState > app.curProcState) { // 更新用户的最高优先级 uidRec.curProcState = app.curProcState; } } } if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.killedByAm) { numTrimming++; // 统计优先级低于Home进程的,表明此更加进程不重要 } } }在updateOomAdjLocked()中主要是计算进程的优先级,同时决定进程的生死,主要任务如下:
根据系统设置的常量计算系统允许的空进程、后台进程的数量,设置空进程、后台进程的adj值遍历所有的后台进程,调用computeOomAdjLocked()计算进程值;对于不能计算的则直接判断是后台进程还是空进程调用applyOomAdjLocked()将进程的等级更新到OOM中倒叙遍历所有进程信息统计后台进程数量,如果超过限定值则直接杀死进程,此处也显示了集合中靠前的进程容易被杀死对于空进程如果超过系统允许存在的数量,且空闲时间超过设置的30min,或总数超过设置的上限将杀死进程对于单独进程的空进程,直接杀死进程统计优先级低于Home进程的数量,保存在numTrimming中本部分主要是处理系统内存紧张时,通知运行的进程回收内存的机制:
首先计算系统中后台进程和空进程的总数,并根据进程总数、空进程数、后台进程数设置系统内存等级,空进程数、后台进程数越少表示系统内存越紧张;当内存等级不为normal时,则表明系统内存紧张需要回收内存,此时需要通知进程释放内存针对不同进程调用app.thread.scheduleTrimMemory()方法执行内存回收,在scheduleTrimMemory()中最终回调到Activity的o nTrimMemory()方法到此Android系统中对进程的管理就分析结束了,简单的说就是会不断计算和更新进程的优先级,然后根据设置的上限条件决定杀死哪些进程保留哪些进程,对于存活的进程Amdroid中也会进行进一步操作,就是通知进程的内存回收,程序根据系统中进程的存活量确定当前内存等级,然后根据进程优先级调用系统的内存回收方法,处理上面的整个流程外,还有一个方法没有分析就是computeOomAdjLocked(),下面看看它是如何计算进程优先级的;
关于应用进程优先级的更新细节见代码注释,源码部分很多请读者自行查看,主要就是根据前台进程、前台服务、后台进程等设置对应的adj的值,在计算完adj之后每个进程都有了各自的优先级顺序,将最终计算的结果保存在app.curAdj中,那此时就代表当前进程的优先级了;
本文从源码的角度分析了AMS是如何管理内存的,主要分析了AMS如何管理应用程序进程管理、OOM Killer机制如何设备中的进程管理,通过对整个内存管理的学习,对系统程序之间的协调运作有了更好的认知,那关于AMS的部分就到此结束了。