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(lrui<mLruProcessActivityStart){
         mLruProcessActivityStart--;
      }
      if(lrui<mLruProcessServiceStart){
          mLruProcessServiceStart--;
      }
      mLruProcesses.remove(lrui);
   }
   int nextIndex;
   if(hasActivity){
       final int N=mLruProcess.size();
       if(app.activities.size()==0&&mLruProcessActivityStart<N-1)){
       //进程中没有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;//返回进程的位置

}

这里先解释空进程和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采取了一种有别于Linux的进程管理策略,有别于Linux的在进程活动停止后就结束该进程Android把这些进程都保留在内存中,直到系统需要更多内存为止。这些保留在内存中的进程通常情况下不会影响整体系统的运行速度,并且当用户再次激活这些进程时,提升了进程的启动速度。 那Android什么时候结束进程?结束哪个进程呢?之前普遍的认识是Android是依据一个名为LRU(last recently used 最近使用过的程序)列表,将程序进行排序,并结束最早的进程。XDA的楼主又进一步对这个管理机制进行研究,有了如下发现:2 V% g, s5 [3 h; J * 系统会对进程的重要性进行评估,并将重要性以“oom_adj”这个数值表示出来,赋予各个进程;(系统会根据“oom_adj”来判断需要结束哪些进程,一般来说,“oom_adj”的值越大,该进程被系统选中终止的可能就越高) * 前台程序的“oom_adj”值为0,这意味着它不会被系统终止,一旦它不可访问后,会获得个更高的“oom_adj”,作者推测“oom_adj”的值是根据软件在LRU列表中的位置所决定的; * Android不同于Linux,有一套自己独特的进程管理模块,这个模块有更强的可定制性,可根据“oom_adj”值的范围来决定进程管理策略,比如可以设定“当内存小于X时,结束“oom_adj”大于Y的进程”。这给了进程管理脚本的编写以更多的选择。 * Android进程分为六大类: o 前台进程(foreground):目前正在屏幕上显示的进程和一些系统进程。举例来说,Dialer Storage,Google Search等系统进程就是前台进程;再举例来说,当你运行一个程序,如浏览器,当浏览器界面在前台显示时,浏览器属于前台进程(foreground),但一旦你按home回到主界面,浏览器就变成了后台程序(background)。我们最不希望终止的进程就是前台进程。 o 可见进程(visible):可见进程是一些不再前台,但用户依然可见的进程,举个例来说:widget、输入法等,都属于visible。这部分进程虽然不在前台,但与我们的使用也密切相关,我们也不希望它们被终止(你肯定不希望时钟、天气,新闻等widget被终止,那它们将无法同步,你也不希望输入法被终止,否则你每次输入时都需要重新启动输入法) o 次要服务(secondary server):目前正在运行的一些服务(主要服务,如拨号等,是不可能被进程管理终止的,故这里只谈次要服务),举例来说:谷歌企业套件,Gmail内部存储,联系人内部存储等。这部分服务虽然属于次要服务,但很一些系统功能依然息息相关,我们时常需要用到它们,所以也太希望他们被终止 o 后台进程(hidden):虽然作者用了hidden这个词,但实际即是后台进程(background),就是我们通常意义上理解的启动后被切换到后台的进程,如浏览器,阅读器等。当程序显示在屏幕上时,他所运行的进程即为前台进程(foreground),一旦我们按home返回主界面(注意是按 home,不是按back),程序就驻留在后台,成为后台进程(background)。后台进程管理策略有多种:有较为积极的方式,一旦程序到达后台立即终止,这种方式会提高程序的运行速度,但无法加速程序的再次启动;也有较消极的方式,尽可能多的保留后台程序,虽然可能会影响到单个程序的运行速度,但在再次启动已启动的程序时,速度会有所提升。这里就需要用户根据自己的使用习惯找到一个平衡点 o 内容供应节点(content provider):没有程序实体,进提供内容供别的程序去用的,比如日历供应节点,邮件供应节点等。在终止进程时,这类程序应该有较高的优先权 o 空进程(empty):没有任何东西在内运行的进程,有些程序,比如BTE,在程序退出后,依然会在进程中驻留一个空进程,这个进程里没有任何数据在运行,作用往往是提高该程序下次的启动速度或者记录程序的一些历史信息。这部分进程无疑是应该最先终止的。 8 e5 H+ z% R) ^0 U# p4 _- E 实践 说完理论,说些实践的东西,怎样管理这六类进程,如何来设置进程管理模块是这部分说的内容。 首先是软件,推荐使用MinFreeManager,市场上就有下载,用于设置这六类进程管理策略。 软件运行后有六个输入框,在输入框中只能输入数字,这些数字代表了这类进程的处理策略,比如Foreground App下的输入框显示6,就表示,当可用内存低于6MB时,终止Foreground App。下面的类似,如Empty App下的输入框显示24,则表示,当内存低于24MB时,终止Empty App。 从软件数值的设置不难看出结束进程的有限顺序:Empty>Content Provider>Hidden>Secondary Server>Visible>Foreground。 但默认设置确存在一些问题: * 各类进程管理策略的阀值相当接近:6,8,16,20,22,24,最大的相差也不到8MB,在实际程序运行中,很容易导致多种类型的进程同时被关闭。如可用内存在25时,突然启动照相程序,系统可用内存急速,可能会导致空进程、内容供应节点、后台进程、次要服务等同时被关闭 * 阀值上限较低:一般手机启动后,可用内存在50-100左右,但随着手机的使用,可用内存会逐步减少,最后降低到24MB左右,则系统开始启动进程管理机制,开始结束进程,但这个阀限制设在了24MB,相对来说偏低。其结果会导致系统使用一段时间后,整体速度变慢。很明显的就是,当手机长时间使用后,开启电话拨号,相册,照相机等应用时,系统的反应速度极慢。 基于以上几个问题,不难看出,我们修改的目标也将非常明确,主要解决两个矛盾: * 拉开各进程的阀值层次,使得进程管理机制能更有效得工作 * 提升阀值上限,空出更多的空余内存,以提升系统整体的运行速度 进程管理策略设置原则: * 前台进程、可见进程和次要服务是与用户体验息息相关的内容,这部分的进程管理策略要相对保守,给这些进程留下足够的运行空间 * 压榨无用进程,腾出内存空间给主要程序使用 下面笔者总结了几种设置方式,适应不同的使用需要: * 游戏玩家/重度浏览器使用者配置: o 用户特点:长时间专注于某一特定的,高内存需求的程序,对多任务的需求不高 o 配置参数: + Foreground:6 + Visible:8 + Secondary Server:16 + Hiden App:80 + Content Provider:90 + Empty:100 o 配置理念:压榨后台进程,内容供应节点和空进程,将内存尽可能多得留给前台进程和系统,提升游戏速度和浏览器体验 o 优点:程序启动和运行的速度最快 o 缺点:多任务处理不理想,开启程序较多时,后台进程会被终止 * 多任务配置: o 用户特点:同时运行多个应用程序,需要经常在多个程序间切换 o 配置参数: + Foreground:6 + Visible:8 + Secondary Server:16 + Hiden App:20 + Content Provider:60 + Empty:100 o 配置理念:压榨空进程,给内容供应节点留有一定空间,最大限度提升后台程序的使用空间,提升多任务的处理能力 o 优点:运行多个程序时,由于可支配内存较多,后台程序不容易被终止 o 缺点:程序启动的速度和整体系统的运行速度可能会比游戏玩家配置略慢一些,由于经常运行多任务,平时系统的响应速度会受到一定影响 * 轻度用户/女生专用配置 o 用户特点:手机的主要功能是短信和电话,偶尔用用相机自拍 o 配置参数: + Foreground:6 + Visible:8 + Secondary Server:16 + Hiden App:24 + Content Provider:32 + Empty:48 o 配置理念:压榨空进程,给内容供应节点留有一定空间,最大限度提升后台程序的使用空间,提升多任务的处理能力 o 优点:比较均衡的配置,提升了系统的可用内存,使得系统的整体速度得到了提高,拉开了各级进程管理策略层次,使得管理机制更有效率 o 缺点:比较均衡的配置,无明显缺点 总结 阐述完了内存管理的机制,并推荐了一些配置参数,但这些参数并不一定适用于所有人,大家也可以根据自己实际的使用习惯调整这些参数的设置。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页