Android 进程管理
在Android中,进程(process)的概念被弱化,传统的进程是程序执行的载体,进程退出意味着应用的关闭。但是在Android中进程知识一个运行组件的容器,当系统需要运行一个组件时,启动包含它的进程,当组件不在使用时,进程也会被关闭。例如一个APK文件中的两个service,可以运行在一个进程中,也可以运行在各自的进程中。虽然在Android的应用开发中,不再强调进程的概念,但是在AMS中,还
在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进程还是空进程后,会按照从低到高的原则把进程放到某个级别中,如果该几倍进程满了,就进入下一个级别。
更多推荐
所有评论(0)