Android(安卓)进程管理
在Android中,进程(process)的概念被弱化,传统的进程是程序执行的载体,进程退出意味着应用的关闭。但是在Android中进程知识一个运行组件的容器,当系统需要运行一个组件时,启动包含它的进程,当组件不在使用时,进程也会被关闭。例如一个APK文件中的两个service,可以运行在一个进程中,也可以运行在各自的进程中。
虽然在Android的应用开发中,不再强调进程的概念,但是在AMS中,还必须管理和调度进程,AMS对进程的管理,主要体现在两方面:一是动态的调整进程在mLruProcesss列表的位置,二是调整进程oom_adj的值,这两项调整和系统进行自动内存回收有关。当内存不足时,系统会关闭一些进程来释放内存。
系统主要根据进程的oom_adj值来挑选要杀死的进程,oom_adj值越大表示进程越可能被杀死。
1. 启动进程
AMS中启动一个进程调用的是addAppLocked()方法,代码如下:
final ProcessResord addAppLocked(ApplicationInfo info, boolean isolated){ ProcessRecord app; //isolated为true表示要启动一个新进程 if(!isolated){ app=getProcessResordLocked(info.processName,info.uid,true); }else{ app=null; } if(app=null){ //创建一个ProcessRecord对象 app=newProcessRecordLocked(info,null,0); mProcessNames.put(info.processName,app.uid,app); if(isolated){ mIsolatedProcesses.put(app.uid,app); } updateLruProcessLocked(app,false,null); updateOomAdjLocked(); } ..... if(app.thred==null&&mPersistentStartingProcesses.indexOf(app)<0){ mPersistentStartingProcesses.add(app); //启动进程 startProcessLocked(app,"added application",app.processName abiOverride,null /*entryPoint */,null /*entryPointArgs */); } return app;}
addAppLocked()方法会根据参数参数isolated来决定是否启动一个新进程,如果isoated为true,即使系统中可能已经有一个同名的进程存在,也会再创建一个新锦成。getProcessRecordLocked()方法用来当前运行的进程列表查找进程。newProcessRecordLocked()方法用来创建一个ProcessRecord的数据结构。updateLruProcessLocked方法用来更新运行进程的状态,updateOomAdjLocked()方法用来更新进程的优先级,这两个方法是Process的管理核心。
首先看看startProcessLocked()是启动进程的方法,看看它的代码:
private final void startProcessLocked(ProcessRecord app,String hostingType,String abiOverride){ if(app.pid>0&&app.pid!=MY_PID){ synchronized(mpidSelfLocked){ mPidSelfLocked.remove(app.pid);//把进程id先移除,防止重复 //把消息PROC_START_TIMEOUT_MSG也清除 mHandler.removeMessages(PROC_START_TIMEOUT_MSG,app); } app.setpid(0); } mProcessOnHold.remove(app); ...... try{ final int uid=app.uid; int[] gids=null; intmountExternal=zygote.MOUNT_EXTERNAL_NONE; if(!app.isolate){ int [] permGids=null; try{ final PackageManager pm=mContext.getPackagesManager(); //检查进程权限,确定它是否能看见所有用户的存储空间 if(Enviroment.isExternalStorageEmulated()){ if(pm.checkPermission(.....)){ mountExternal=Zygote.MOUNTEXTERNAL_MULTIUSER_ALL; else{ mountExternal=Zygote.MOUNTEXTERNAL_MULTIUSER; } } }catch (PackageManager.NameNotFountException e){.} } ...... //启动应用 Process.ProcessStartResult startResult=Process.start( app.processName,uid,uid,gids,debugFlags, mountExternal,app.info.targetSdkVersion, app.info.seinfo,requiredAbi,instructionSet, app.infodataDir,entryPointArgs) ..... synchornized(mPidSelfLocked){ //发送一个定时消息,时间到应用还没启动完成就会出现ANR this.mPidSelfLocked.put(startResult.pid,app); if(isActivityProcess){ Message msg=mHandler.obtainMessage( PROC_START_TIME_OUT_MSG); msg.obj=app; mHandler.sendMessageDelayed(msg,startResult .usingWrapper?PROC_START_WITH_WRAPPER: PROC_STRAT_TIMEOUT); } } }catch(RuntimeException e){ app.setpid(0); mBatteryStatsService.noteProcessFinish( app.processName,app.info.uid); if(app.isolated){ mBatteryStatsService.removeIsolatedUid( app.uid,app.info.uid); } } }}
startProcessLocked()方法的流程是,准备好启动应用的参数,调用Process类的start来启动进程,启动进程后AMS给自己发送了一个PROC_START_TIMEOUT_MSG的消息,来防止进程启动超时。如果start()函数返回的结果中usingWrapper的值为true,超时时间设为1200秒。
static final int PROC_START_TIMEOUT_WITH_WRAPPER=1200*1000;
否则超时时间设为10秒。
static final int PROC_START_TIMEOUT=10*1000;
如果时间到了,但是进程还没启动完成,AMS将弹出发生ANR的对话框。
2. 调整进程的位置
AMS的代码中经常调用updateLruProcessLocked()方法来调整某个进程在mLruProcesses列表的位置,mLruProcess是最近使用进程列表(List Of Recent Using的缩写)。每当进程的Activity或者Service发生变化时,意味着进程活动发生了活动,因此,调用这个方法将调整到尽可能最高的位置,同时还要更新关联进程的位置。在mLruProcesses列表中,最近活动的进程总是位于最高位置。同时拥有Activity的进程的位置总是高于只有Service的进程位置。
AMS的成员变量mLruProcessActivityStart和mLruProcessServiceStart分别指向位置最高的、带有Activity进程和没有Activity进程。
updateLruProcessLocked()方法的代码如下:
final void updateLruProcessLocked(ProcessRecord app,boolean activityChange, ProcessRecord client){ //app.activities.size()大于0,表示本进程有活动的Activity。 //app.hasClientActivities的值为true,表示绑定了本进程的Service的 //客户进程有活动的Activity //treatLikeActivity表示Service启动时带有标记BIND_TREAT_LIKE_ACTVITY final boolean hasActivity=app.activities.size()>0 ||app.hasClientActivities||treatLikeActivity; final boolean hasService=false; if(!activityChange&&hasActivity){ //如果ProcessRecord对象已经有了Activity //再调用本方法,除非是Activity发生变化了才要 return; } mLruSeq++; final long now=SystenClock.uptimeMillis(); app.lastActivityTime=now;//更新lastActivityTime中的时间 if(hasActivity){ //如果进程已经初始化,而且在mLruProcesses列表位置也是最后一项 //这样也没什么可做的,退出 final int N=mLruProcesses.size(); if(N>0&&mLruProcesses.get(N-1)==app){ return ; } }else{ //如果进程中没有Activity,而且应景位于mLruProcesses列表的合适位置,退出 if(mLruProcessServiceStart>0&&mLruProcesses.get(mLruProcessServiceStart-1)==app){ return ; } } int lrui=mLruProcess.lastIndexOf(app); if(app.persistent&&lrui>=0){ return;//带有persistent标志的进程不需要调整,退出 } if(lrui>=0){ //如果进程已经存在,先从mLruProceses列表中移除,同时调整mLruProcessActivityStart和mLruProcessServiceStart指向的位置 if(lruiif(lruiint nextIndex; if(hasActivity){ final int N=mLruProcess.size(); if(app.activities.size()==0&&mLruProcessActivityStart1)){ //进程中没有Activity,但是它的Service客户进程中有Activity mLruProcesses.add(N-1,app);//将进程插入到最后一项 final int uid=app.info.uid; //如果从倒数第三项开始连续有进程的uid和插入的进程uid相同,把他们的 位置向上移动 for(int i=N-2;i>mLruProcessActivityStart;i--){ processRecord subProc =mPruProcess.get(i); if(subProc.info.uid==uid){ if(mLruProcesses.get(i).info.uid!=uid){ mLruProcesses.set(i,mLruProcesses.get(i-1)); mLruProcesses.set(i-1,temp); i--; } }else{ break; } } } else{ mLruProcesses.add(app);//进程有Activity,加入到最后一项 } nextIndex=mLruProcessesServiceStart;//关联进程将要插入的位置 }else if(hasService){ .....//hasService总是为False,这段不会执行 }else{ //如果进程中只有Service,将进程插入到mLruProcessServiceStart指向的位置 int index=mLruProcessServiceStart; if(client!=null){ ......//基本上为null } mLruProcess.add(index,app); nextIndex=index-1; //关联进程插入的位置 mLruProcessActivityStart++; mLruProcessServiceStart++; } //将和本进程的Service关联的客户进程的位置调整到本进程之后 for(int j=app.connections.size()-1;j>=0;j--){ ConnectionRecord cr=app.connections.valueAt(j); if(cr.binding!=null&&!crserviceDead &&cr.binding.service!=null&&cr.binding.service.app !=null &&cr.binding.service.app.lruSeq!=mLruq &&!cr.binding.service.app.persistent){ nextIndex=updateLruProcessInternalLocked( cr.binding.service.app,now,nextIntext, "service connection",cr,app); } } //将和本进程ContentProvider关联的客户进程的位置调整到本进程之后 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,nextIntext, "provider reference",cpr,app); } }}
updateLruProcessLocked()方法中调整进程很重要的一个依据是进程中有没有活动的Activity。除了本身进程存在Activity对象之外,如果和进程中运行的Service相关联的客户进程有Activity,也算进程拥有Activity,这里调整位置的目的是为了将来杀死进程释放内存做准备,如果一个进程关联进程Activity对象存在,那么它的重要性也和真正的和拥有Activity对象的进程相当,如果杀死它,将导致另一个进程出现严重错误,Activity用来显示UI,关系着用户体验,因此尽量不关闭运行Activity组件的进程。
如果一个进程“拥有”Activity,通常会把它插到队列的最高位置,否则,只会把它放到所有没有Activity进程的前面,这个位置正是变量mLruProcessServiceStart所指向的。
调整某个进程的位置之后,还要调整和该进程的关联的位置。进程的关联进程有两种类型:一种是绑定了本进程服务的进程,另一种是连接了本进程的ContentProvider的进程。如果这些进程本身有Activity是不会调整的,需要调整的是那些没有Activity的进程,在updateLruProcessInternalLocked()方法中会执行这种调整,但是,能调整到最高位置也就是mLruProcessServiceStart指向的位置。
updateLruProcessInternalLocked()方法代码如下:
private final int updateLruProcessInternalLocked(ProcessRecord app,long now,int index,String what,Object obj,ProcessRecord srcApp){ app.lastActivityTime=now; if(app.activities.size()>0){ return index;//如果有Activity,不用调整位置。 } int lrui=mLruProcesses.lastIndexOf(app); if(lrui<0){ return index;//如果进程不在mLruProcesses中,退出 } if(lrui>=index){ return index;//如果进程目前的位置高于要调整的位置,退出 } if(lrui>=mLruProcessActivityStart){ return index; //如果进程目前的位置比有Activity的进程还高,退出 } //把进程调整到参数index-1的位置 mLruProcesses.remove(this); if(index>0){ index--; } mLruProcesses.add(index,app); return index;//返回进程的位置}
3. ProcessList的常量
在ProcessList中定义了大量AMS中用到的常量,看看他们的定义
final class ProcessList{ //定义发生crash最小时间间隔,如果进程小于这个时间内发生crash,会被认为坏进程 static final int MIN_CRASH_INTERVAL=60*1000; //处于某种不可知状态进程的oom_adj值 static final int UNKOWN_ADJ=16; //cahhed进程的的oom_adj最大和最小值定义 static final int CACHE_APP_MAX_ADJ=15; static final int CHCHE_APP_MIN_ADJ=9; //位于B列表的服务进程oom_adj值,位于B列表的都是一些旧的、过时的服务进程 static final int SERVICE_B_ADJ=8; //当前Activity的前一个Activity所处进程的oom_adj值 static final int PREVIOUS_APP_ADJ=7; //Home进程oom_adj值 static final int HOME_APP_ADJ=6; //包含组件Service的进程oom_adj static final int SERVICE_ADJ=5; //heavy-weight进程的oom_adj值 static final int HEAVY_WEIGH_APP_ADJ=4; //正在执行的backup的进程oom_adj值 static final int BACKUP_APP_adj=3; //不在前台但是包含有用户可感知组件的进程的oom_adj值(例如播放音乐的后台进程) static final int PERCEPTIBLE_APP_ADJ=2; //仅包含Activity的可见进程的oom_adj的值 static final int VISIBLE_APP_ADJ=1; //前台进程的oom_adj值 static final int FOREGROUND_APP_ADJ=0; //带有PERSISTENT标记而且还有组件Service的进程的oom_adj值 static final int PERSISTENT_SERVICE_ADJ=-11; //死亡后会重启的PERSISTENT进程oom_adj值 static final int PERSISTENT_PROC_ADJ=-12; //系统进程oom_adj static final int SYSTEM_ADJ=-16; //包含native层代码进程的oom_adj static final int NATIVE_adj=-17; //定义内存页面大小为4KB static final int PACKAGE_SIZE=4*1024; //系统最少处于cached状态的进程数量 static final int MIN_CACHED_APPS=2; //系统最大处于cached状态的进程数量 static final int MAX_CACHED_APPS=32; //定义空进程最大保存时间为30分钟 static final int MAX_EMPTY_TIME=30*60*1000; //定义最大的空进程数量。它的值为MAX_CACHED_APPS的2/3 static final int MAX_EMPTY_APPS= computeEmptyProcessLimit(MAX_CACHED_APPS); //开始内存回收空进程的阈值。系统空进程进程数量低于这个值不会执行内存回收。 static final int TRIM_EMPTY_APPS=MAX_EMPTY_APPS/2; //开始内存回收cached的阈值。系统cached进程数量低于这个值不会执行内存回收。 static final int TRIM_CACHED_APPS=(MAX_CACHED_APPS-MAX_EMPTY_APPS)/3; //定义内存回收的oom_adj阈值 private final int mOomAdj=new init[]{ FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ,PERCEPTIBLE_APP_ADJ, BACKUP_APP_adj,CHCHE_APP_MIN_ADJ,CACHE_APP_MAX_ADJ }; //定义用于低配置(HVGA或者更低,内存小于512M)设备内存回收阈值 private final int mOomMinFreeLow=new int[]{ 12288,18432,24576, 36864,43008,49152 }; //定义高分辨率内存回收阈值 private final int mOonMinFreeHigh=new int []{ 73728.92160,110592, 129024,147456,184320 }; ......}
这里先解释空进程和cached进程的概念,如果一个进程不包含任何组件,该进程可以认为是空进程,例如,一个进程只包含一个Activity,当这个Activity销毁后变成一个”空”进程。
当Android结束一个进程时,并不会将一个进程立即从系统删除,而是把它标记为cached进程,当再次启动新进程时,优先会使用cached进程吧,这样会加快启动速度。
4. 调整进程的oom_adj值
AMS中调整进程的oom_adj值的方法是updateOomAdjLocked(),代码如下:
final void updateOomAdjLocked() { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; final long now = SystemClock.uptimeMillis(); final long nowElapsed = SystemClock.elapsedRealtime(); final long oldTime = now - ProcessList.MAX_EMPTY_TIME; final int N = mLruProcesses.size(); if (false) { RuntimeException e = new RuntimeException(); e.fillInStackTrace(); Slog.i(TAG, "updateOomAdj: top=" + TOP_ACT, e); } // Reset state in all uid records. for (int i=mActiveUids.size()-1; i>=0; i--) { final UidRecord uidRec = mActiveUids.valueAt(i); if (false && DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Starting update of " + uidRec); uidRec.reset(); } mStackSupervisor.rankTaskLayersIfNeeded(); mAdjSeq++; mNewNumServiceProcs = 0; mNewNumAServiceProcs = 0; final int emptyProcessLimit; final int cachedProcessLimit; if (mProcessLimit <= 0) { emptyProcessLimit = cachedProcessLimit = 0; } else if (mProcessLimit == 1) { emptyProcessLimit = 1; cachedProcessLimit = 0; } else { emptyProcessLimit = ProcessList.computeEmptyProcessLimit(mProcessLimit); cachedProcessLimit = mProcessLimit - emptyProcessLimit; } // Let's determine how many processes we have running vs. // how many slots we have for background processes; we may want // to put multiple processes in a slot of there are enough of // them. int numSlots = (ProcessList.CACHED_APP_MAX_ADJ - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2; int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs; if (numEmptyProcs > cachedProcessLimit) { // If there are more empty processes than our limit on cached // processes, then use the cached process limit for the factor. // This ensures that the really old empty processes get pushed // down to the bottom, so if we are running low on memory we will // have a better chance at keeping around more cached processes // instead of a gazillion empty processes. numEmptyProcs = cachedProcessLimit; } int emptyFactor = numEmptyProcs/numSlots; if (emptyFactor < 1) emptyFactor = 1; int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots; if (cachedFactor < 1) cachedFactor = 1; int stepCached = 0; int stepEmpty = 0; int numCached = 0; int numEmpty = 0; int numTrimming = 0; mNumNonCachedProcs = 0; mNumCachedHiddenProcs = 0; // 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; ProcessRecord selectedAppRecord = null; long serviceLastActivity = 0; int numBServices = 0; for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); if (app == null) { continue; } if (mEnableBServicePropagation && app.serviceb && (app.curAdj == ProcessList.SERVICE_B_ADJ)) { numBServices++; for (int s = app.services.size() - 1; s >= 0; s--) { ServiceRecord sr = app.services.valueAt(s); if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + app.processName + " serviceb = " + app.serviceb + " s = " + s + " sr.lastActivity = " + sr.lastActivity + " packageName = " + sr.packageName + " processName = " + sr.processName); if (SystemClock.uptimeMillis() - sr.lastActivity < mMinBServiceAgingTime) { if (DEBUG_OOM_ADJ) { Slog.d(TAG,"Not aged enough!!!"); } continue; } if (serviceLastActivity == 0 && app != TOP_APP) { serviceLastActivity = sr.lastActivity; selectedAppRecord = app; } else if (sr.lastActivity < serviceLastActivity && app != TOP_APP) { serviceLastActivity = sr.lastActivity; selectedAppRecord = app; } } } if (DEBUG_OOM_ADJ && selectedAppRecord != null) Slog.d(TAG, "Identified app.processName = " + selectedAppRecord.processName + " app.pid = " + selectedAppRecord.pid); if (!app.killedByAm && app.thread != null) { app.procStateChanged = false; computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now); // If we haven't yet assigned the final cached adj // to the process, do that now. if (app.curAdj >= ProcessList.UNKNOWN_ADJ) { switch (app.curProcState) { case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY: case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT: // This process is a cached process holding activities... // assign it the next cached value for that type, and then // step that cached level. app.curRawAdj = curCachedAdj; app.curAdj = app.modifyRawOomAdj(curCachedAdj); if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning activity LRU #" + i + " adj: " + app.curAdj + " (curCachedAdj=" + curCachedAdj + ")"); if (curCachedAdj != nextCachedAdj) { stepCached++; if (stepCached >= cachedFactor) { stepCached = 0; curCachedAdj = nextCachedAdj; nextCachedAdj += 2; if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) { nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ; } } } break; default: // For everything else, assign next empty cached process // level and bump that up. Note that this means that // long-running services that have dropped down to the // cached level will be treated as empty (since their process // state is still as a service), which is what we want. app.curRawAdj = curEmptyAdj; app.curAdj = app.modifyRawOomAdj(curEmptyAdj); if (DEBUG_LRU && false) Slog.d(TAG_LRU, "Assigning empty LRU #" + i + " adj: " + app.curAdj + " (curEmptyAdj=" + curEmptyAdj + ")"); if (curEmptyAdj != nextEmptyAdj) { stepEmpty++; if (stepEmpty >= emptyFactor) { stepEmpty = 0; curEmptyAdj = nextEmptyAdj; nextEmptyAdj += 2; if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) { nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ; } } } break; } } applyOomAdjLocked(app, true, now, nowElapsed); // Count the number of process types. 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 > ProcessList.TRIM_EMPTY_APPS && app.lastActivityTime < oldTime) { app.kill("empty for " + ((oldTime + ProcessList.MAX_EMPTY_TIME - app.lastActivityTime) / 1000) + "s", true); } else { numEmpty++; if (numEmpty > emptyProcessLimit) { app.kill("empty #" + numEmpty, true); } } break; default: mNumNonCachedProcs++; break; } if (app.isolated && app.services.size() <= 0) { // If this is an isolated process, and there are no // services running in it, then the process is no longer // needed. We agressively kill these because we can by // definition not re-use the same process again, and it is // good to avoid having whatever code was running in them // left sitting around after no longer needed. app.kill("isolated not needed", true); } else { // Keeping this process, update its uid. final UidRecord uidRec = app.uidRecord; if (uidRec != null && uidRec.curProcState > app.curProcState) { uidRec.curProcState = app.curProcState; } } if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.killedByAm) { numTrimming++; } } } if ((numBServices > mBServiceAppThreshold) && (true == mAllowLowerMemLevel) && (selectedAppRecord != null)) { ProcessList.setOomAdj(selectedAppRecord.pid, selectedAppRecord.info.uid, ProcessList.CACHED_APP_MAX_ADJ); selectedAppRecord.setAdj = selectedAppRecord.curAdj; if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + selectedAppRecord.processName + " app.pid = " + selectedAppRecord.pid + " is moved to higher adj"); } mNumServiceProcs = mNewNumServiceProcs; // Now determine the memory trimming level of background processes. // Unfortunately we need to start at the back of the list to do this // properly. We only do this if the number of background apps we // are managing to keep around is less than half the maximum we desire; // if we are keeping a good number around, we'll let them use whatever // memory they want. final int numCachedAndEmpty = numCached + numEmpty; int memFactor; if (numCached <= ProcessList.TRIM_CACHED_APPS && numEmpty <= ProcessList.TRIM_EMPTY_APPS) { if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) { memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL; } else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) { memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW; } else { memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE; } } else { memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL; } // We always allow the memory level to go up (better). We only allow it to go // down if we are in a state where that is allowed, *and* the total number of processes // has gone down since last time. if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor + " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel + " numProcs=" + mLruProcesses.size() + " last=" + mLastNumProcesses); if (memFactor > mLastMemoryLevel) { if (!mAllowLowerMemLevel || mLruProcesses.size() >= mLastNumProcesses) { memFactor = mLastMemoryLevel; if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!"); } } if (memFactor != mLastMemoryLevel) { EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel); } mLastMemoryLevel = memFactor; mLastNumProcesses = mLruProcesses.size(); boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !isSleepingLocked(), now); final int trackerMemFactor = mProcessStats.getMemFactorLocked(); if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) { if (mLowRamStartTime == 0) { mLowRamStartTime = now; } int step = 0; int fgTrimLevel; switch (memFactor) { case ProcessStats.ADJ_MEM_FACTOR_CRITICAL: fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL; break; case ProcessStats.ADJ_MEM_FACTOR_LOW: fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW; break; default: fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE; break; } int factor = numTrimming/3; int minFactor = 2; if (mHomeProcess != null) minFactor++; if (mPreviousProcess != null) minFactor++; if (factor < minFactor) factor = minFactor; int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); /* yulong begin add */ /* FEATURE_APP_FREEZE_BACKGROUND */ /* add for app freeze, liuyaxin, 20170519*/ if(ZSFeature.ZEUSIS_FEATURE_APP_FREEZE_BACKGROUND){ if(app.frozenRemoved){ Slog.d(TAG, app.info.packageName + " is frozen and no need to scheduleTrimMemory[updateOomAdjLocked memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL]"); continue; } } /* yulong end */ if (allChanged || app.procStateChanged) { setProcessTrackerStateLocked(app, trackerMemFactor, now); app.procStateChanged = false; } if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.killedByAm) { if (app.trimMemoryLevel < curLevel && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, "Trimming memory of " + app.processName + " to " + curLevel); app.thread.scheduleTrimMemory(curLevel); } catch (RemoteException e) { } if (false) { // For now we won't do this; our memory trimming seems // to be good enough at this point that destroying // activities causes more harm than good. if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE && app != mHomeProcess && app != mPreviousProcess) { // Need to do this on its own message because the stack may not // be in a consistent state at this point. // For these apps we will also finish their activities // to help them free memory. mStackSupervisor.scheduleDestroyAllActivities(app, "trim"); } } } app.trimMemoryLevel = curLevel; step++; if (step >= factor) { step = 0; switch (curLevel) { case ComponentCallbacks2.TRIM_MEMORY_COMPLETE: curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE; break; case ComponentCallbacks2.TRIM_MEMORY_MODERATE: curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND; break; } } } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) { if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, "Trimming memory of heavy-weight " + app.processName + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); app.thread.scheduleTrimMemory( ComponentCallbacks2.TRIM_MEMORY_BACKGROUND); } catch (RemoteException e) { } } app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND; } else { if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND || app.systemNoUi) && app.pendingUiClean) { // If this application is now in the background and it // had done UI, then give it the special trim level to // have it free UI resources. final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN; if (app.trimMemoryLevel < level && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui " + app.processName + " to " + level); app.thread.scheduleTrimMemory(level); } catch (RemoteException e) { } } app.pendingUiClean = false; } if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName + " to " + fgTrimLevel); app.thread.scheduleTrimMemory(fgTrimLevel); } catch (RemoteException e) { } } app.trimMemoryLevel = fgTrimLevel; } } } else { if (mLowRamStartTime != 0) { mLowRamTimeSinceLastIdle += now - mLowRamStartTime; mLowRamStartTime = 0; } for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); /* yulong begin add */ /* FEATURE_APP_FREEZE_BACKGROUND */ /* add for app freeze, liuyaxin, 20170519*/ if(ZSFeature.ZEUSIS_FEATURE_APP_FREEZE_BACKGROUND){ if(app.frozenRemoved){ Slog.d(TAG, app.info.packageName + " is frozen and no need to scheduleTrimMemory[updateOomAdjLocked memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL]"); continue; } } /* yulong end */ if (allChanged || app.procStateChanged) { setProcessTrackerStateLocked(app, trackerMemFactor, now); app.procStateChanged = false; } if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND || app.systemNoUi) && app.pendingUiClean) { if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN && app.thread != null) { try { if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, "Trimming memory of ui hidden " + app.processName + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); app.thread.scheduleTrimMemory( ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); } catch (RemoteException e) { } } app.pendingUiClean = false; } app.trimMemoryLevel = 0; } } if (mAlwaysFinishActivities) { // Need to do this on its own message because the stack may not // be in a consistent state at this point. mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish"); } if (allChanged) { requestPssAllProcsLocked(now, false, mProcessStats.isMemFactorLowered()); } // Update from any uid changes. for (int i=mActiveUids.size()-1; i>=0; i--) { final UidRecord uidRec = mActiveUids.valueAt(i); int uidChange = UidRecord.CHANGE_PROCSTATE; if (uidRec.setProcState != uidRec.curProcState) { if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "Changes in " + uidRec + ": proc state from " + uidRec.setProcState + " to " + uidRec.curProcState); if (ActivityManager.isProcStateBackground(uidRec.curProcState)) { if (!ActivityManager.isProcStateBackground(uidRec.setProcState)) { uidRec.lastBackgroundTime = nowElapsed; if (!mHandler.hasMessages(IDLE_UIDS_MSG)) { // Note: the background settle time is in elapsed realtime, while // the handler time base is uptime. All this means is that we may // stop background uids later than we had intended, but that only // happens because the device was sleeping so we are okay anyway. mHandler.sendEmptyMessageDelayed(IDLE_UIDS_MSG, BACKGROUND_SETTLE_TIME); } } } else { if (uidRec.idle) { uidChange = UidRecord.CHANGE_ACTIVE; uidRec.idle = false; } uidRec.lastBackgroundTime = 0; } uidRec.setProcState = uidRec.curProcState; enqueueUidChangeLocked(uidRec, -1, uidChange); noteUidProcessState(uidRec.uid, uidRec.curProcState); } } if (mProcessStats.shouldWriteNowLocked(now)) { mHandler.post(new Runnable() { @Override public void run() { synchronized (ActivityManagerService.this) { mProcessStats.writeStateAsyncLocked(); } } }); } if (DEBUG_OOM_ADJ) { final long duration = SystemClock.uptimeMillis() - now; if (false) { Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms", new RuntimeException("here").fillInStackTrace()); } else { Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms"); } } }
updateOomAdjLocked()方法通过调用computeOomAdjLocked()方法来计算进程的oom_adj的值。如果进程的curAdj变量的值仍然大于等于系统预定义的最大oom_adj值(UNKNOW_ADJ),则表明该进程属于“cached”进程或者空进程,updateOomAdjLocked()方法将会为该进程分配oom_adj值。如果用来表示进程状态的变量curProcState的值为PROCESS_STATE_CACHED_ACTIVITY或者PROCESS_STATE_CACHED_ACTIVITY_CLIENT,这说明进程是cached进程,否则为空进程。
updateOomAdjLocked()方法中根据系统定义的cached进程的最大和最小oom_adj的值,先计算出slot的数量,后计算每个slot需要容纳的cached进程的数量cachedFactor和空进程的数量emptyFactor,这样做的目的为了将系统中cached进程和空进程分成不同的级别,每个级别有相同的oom_adj值,级别和级别之间的oom_adj值为2.因此,updateOomAdjLocked()方法区分某个进程是cached进程还是空进程后,会按照从低到高的原则把进程放到某个级别中,如果该几倍进程满了,就进入下一个级别。
更多相关文章
- Android开发指南 ──应用程序基础
- Android进程管理机制及优化(HTC&其它可参考)
- Android中的线程处理
- Android线程模型解析(包括UI的更新)
- Android内核详解之Low memory killer
- Android初学的学习笔记
- Android最全面试题71道题 详解
- Android(安卓)应用程序基础(Application Fundamentals)
- Android(安卓)进程保活招式大全(转)