提起android的進程回收機制,你們所熟知的是Android的lowmemroykiller的機制。當系統可用內存低於某個閥值時,即會殺死這個閥值對應的Adj值的全部應用。可是本篇文章併爲是要介紹Lowmemorykiller的機制,而是要搞清楚在未觸發Android低殺機制時,Android是否有某種策略對進程進行回收。由於隨着硬件成本低下降,各品牌手機低配置也是愈來愈高,4G及6G內存的手機層出不窮,在這類高內存配置的手機下,按照lowmemorykiller的默認參數來看,低殺出現的機率也小了不少。經過對AMS中的updateOomAdjLocked()方法的分析便可瞭解Android在lowmemorykiller以外的Android進程回收機制。java
那咱們按順序來介紹updateOomAdjLocked方法。首先時初始化一些變量用之後續的操做。TOP_ACT和TOP_APP表示當前處於前臺的app(若無app 處於前臺則是TASK棧棧頂的應用);根據LRU列表獲得當前系統的存在的進程數量N;重置用戶組中的進程狀態的動做。TOP_ACT,rankTaskLayersIfNeeded方法,mAdjSeq這些變量主要是用於後面方法中會待調用的computateOomAdjLocked()方法,這不是咱們本篇的重點,因此再也不展開進行詳解。android
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(); ... // 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;
mProcessLimit表示的是系統容許保留的後臺進程和empty進程的總和,初始化爲ProcessList.MAX_CACHED_APPS常量,默認爲32。Android自身的機制在運行中不會主動修改這個值,但用戶能夠在「開發者選項」的「後臺進程限制」這一項來修改該值。emptyProcessLimit和cachedProcessLimit分別爲根據mProcessLimit的值計算出的容許的後臺進程和empty進程的最大數量。當mProcessLimit不小於0且不等於1時,後臺進程和empty進程和後臺進程的數量各佔mProcessLimit的一半。 本文講的後臺進程是指cached進程裏的非empty的進程,而不包含後臺服務進程。web
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; }
再往下是初始化一些變量的操做,這裏要重點注意numSlots所表達的意思。ProcessList.CACHED_APP_MAX_ADJ和Process.CACHED_APP_MIN_ADJ常量系統默認的值分別爲906和900,表示的是後臺進程和empty進程分配的值是在900至906之間,共有7個值。numSloas計算過程當中除以2是由於每一個槽配置到進程包含後臺進程和empty進程兩種,兩種進程需用不一樣adj值表示,因此每一個槽包含兩個adj值的分配空間,因此須要除以二,計算出來的numSlots值爲3。emptyFactor表示每一個槽中須要放置幾個empty進程,是根據當前empty進程總數決定的,cachedFactor便是表示須要放置幾個後臺進程到每一個槽中。app
爲便於理解,結合後面代碼邏輯舉例來說,好比後臺此時有15個cahcedProcess(後臺進程)和12個emptyProcess,則會將15/3=5個cachedProcess設置到在第一個槽(可分配900,901這兩個oom_adj值)中,oom_adj設置爲900;將12/4=4個emptyProcess設置在第一槽,oom_adj值設置爲901;而後再設置5個cachedProcess和4個emptyProcess的oom_adj值分別爲902和903,即第二個槽。 less
// 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;
接下來是對mLruProcesses列表進行遍歷,經過遍歷的過程會更新每個進程的Adj值。遍歷過程當中首先會先調用computOomAdjLocked方法進行oom_adj的計算,在該方法中其實已經完成了cachedProcess和emptyProcess進程之外的進程的oom_adj值的計算,而對於未完成計算的這兩類進程會進入到下面的if (app.curAdj >= ProcessList.UNKNOWN_ADJ)的邏輯中,該圖中已省略去,後文再講。再往下調用了applyOomAdjLocked的邏輯來更新計算好的oom_adj值。再往下進入switch循環,這個循環裏的邏輯便可解答咱們在文中開頭提出的問題。最後是一個針對isolated進程的特殊的處理。 ui
// First update the OOM adjustment for each of the
// application processes based on their current state.
int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextCachedAdj = curCachedAdj+1;
int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextEmptyAdj = curEmptyAdj+2;
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
// If we haven't yet assigned the final cached adj
// to the process, do that now.
if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
...
}
applyOomAdjLocked(app, true, now, nowElapsed);
// Count the number of process types.
switch (app.curProcState) {
...
}
if (app.isolated && app.services.size() <= 0) {
...
}
if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
&& !app.killedByAm) {
numTrimming++;
}
}
}
首先來看if (app.curAdj >= ProcessList.UNKNOWN_ADJ)裏的邏輯,便是對cachedProcess和emptyProcess進程的oom_adj計算的過程。當進程爲包含activity的cached進程即文中所表達的後臺進程或者其客戶端進程時,設置該進程的adj值爲curCachedAdj,curAdj初始爲Process.CACHED_APP_MIN_ADJ,即900。分配的邏輯前文已經提到過,若是時15個CachedProcess,則cachedFactor爲15,分配5個cachedProcess進程的oom_adj值爲900後,增長curCachedAdj值到902,再分配5個cachedProcess的oom_adj值爲902,依次類推到分配完。在該switch的邏輯中default處理的是emptyProcess進程的oom_adj計算方式,與cachedProcess類似,這裏再也不貼出敘述了。this
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: ...//處理empty進程,方式與上面的cachedProcess類似 break; }
再來看switch (app.curProcState)邏輯中的處理,這裏完成的主要就是對比當前cachedProcess和emptyProcess的數量是否超過這兩類進程的限制值,若是超過了則調用kill方法殺掉該進程。因爲這是在便利LruProcesses列表中完成的,遍歷過程是由最近的進程到最遠的進程的順序來完成的,因此當遍歷到越久遠到進程時numCached的值越大,則越容易超過限制值。spa
由此,咱們能夠了解到Android系統在Lowmemeorykill以外的回收機制是:code
1.只會對於後臺進程和空進程進行回收blog
2.後臺進程和空進程的系統容許的最大限制值爲ProcessList.MAX_CACHED_APPS常量的一半
3.當後臺進程數或空進程數超過限制值時會對最久不使用的進程進行回收(調用kill方法),直到這兩類進程數量很少於各自的進程限制值。
// 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; }
在LruProcess遍歷的最後是對isolated process單獨的處理,對於設置了isolated屬性的進程爲true的進程若是已經不包含服務了,則馬上kill掉該進程,最後是記錄當前adj值大於等於桌面進程的全部進程數量,保存到numTrimming中。
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++; }
雖然已經瞭解完android在非lowmemory狀況下的內存回收機制,咱們繼續來看完updateOomAdjLocked方法在更新完adj值後續的動做。首先根據當前的cachedProcess和emptyProcess進程的數量來綜合斷定當前進程的等級,這兩類進程的數量越少,表示系統內存越緊張,內存等級越高。由低至高(對應的數值也是由低至高)分別爲ADJ_MEM_FACTOR_NORMAL、ADJ_MEM_FACTOR_MODERATE、ADJ_MEM_FACTOR_LOW、ADJ_MEM_FACTOR_CRITICAL這四個等級。
爲何能根據後臺進程和空進程數量來判斷出系統的內存等級呢?由於根據以前的分析能夠知道,Android系統在後臺進程和空進程不超過數量上限時老是儘量多的保留後臺進程和空進程,這樣用戶即可再再次啓動這些進程時減小啓動時間從而提升了用戶體驗;而lowmemeorykiller的機制又會在系統可用內存不足時殺死這些進程,因此在後臺進程和空進程數量少於必定數量時,便表示了系統以及觸發了lowmemrorykiller的機制,而剩餘的後臺進程和空進程的數量則正好體現了Lowmemroykiller殺進程的程度,即表示當前系統內存的緊張程度。
// 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!"); } }
接下來是系統內存等級不爲ADJ_MEM_FACTOR_NORMAL時的處理,此時全部的進程都須要進行內存回收操做,後面的處理就是針對不一樣的進程獲得對應的進程內存回收等級,並按照該等級執行進程內的內存回收操做。斷定完進程的回收等級以後,計算出factor變量。這裏的factor的計算方式與以前計算oom_adj時的槽的方式類似。則後面遍歷時前四個進程屬於同一個槽使用同一等級進行回收,以後四個再使用下一個等級進行回收。
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--) {
...
} }
展開for (int i=N-1; i>=0; i--)循環,裏面是對mLruProcesses進程遍歷,順序是從最近的進程向最久遠的進程進行遍歷。
首先是遍歷過程當中重要性低於ActivityManager.PROCESS_STATE_HOME的進程的處理,包括B-Service進程、cachedProcess和emptyProcess。因爲curLevel設置的初始值爲最高等級,而在遍歷mLruProcess過程當中curLevel會逐漸減小,結合遍歷是由近至遠這裏涵蓋的思想就是越近的進程adj值會越高(相同類別),而adj值越高的進程越不容易被lowmemroykiller機制殺死,這樣這類越不容易殺死的進程就越應該執行高回收等級的回收動做。
for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); 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) { ... } else { ... } }
以後是對重要性大於PROCESS_STATE_HOME的PROCESS_STATE_HEAVY_WEIGHT進程的處理,只有當進程自己的回收等級低於TRIM_MEMORY_BACKGROUND時,纔再執行TRIM_MEMORY_BACKGROUND的等級的回收
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; }
對於重要性高於PROCESS_STATE_HOME的其餘類別的進程,其中若是重要性低於PROCESS_STATE_IMPORTANT_BACKGROUND則以TRIM_MEMORY_UI_HIDDEN的等級進行進程內的回收;若是進程自身的回收等級低於當前回收等級fgTrimLevel,則對該進程以當前回收等級進行回收。這裏的意思是,避免屢次執行回收,這類重要性高於HOME的進程,只有在內存變得更緊時,才以更高的等級進行回收一次。
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; }
再來看,若是根據cachedProcess和emptyProcess數量計算出的系統內存等級爲正常時候的處理。簡單來講,此時只須要讓重要性低於PROCESS_STATE_IMPORTANT_BACKGROUND以TRIM_MEMORY_UI_HIDDEN進行回收便可。
if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) { ... } else { if (mLowRamStartTime != 0) { mLowRamTimeSinceLastIdle += now - mLowRamStartTime; mLowRamStartTime = 0; } for (int i=N-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); 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; } }
方法的最後是一些掃尾和保存相關狀態變化的工做,好比配合開發者模式的選項判斷時候執行activity等,這裏再也不展開介紹。
總結一下,updateOomAdjLocked方法主要完成了三個動做:
1、更新全部應用等oom_adj值,其中非cachedProcess和emptyProcess進程等oom_adj值的計算經過調用computeOomAdjLocked方法完成,而cachedProcess和emptyProcess進程則按照由近至遠的方式交替從小至大的分配oom_adj值
2、根據cachedPrcess和emptyProcess的數量判斷是否超過數量上限,超過則殺死進程,越久遠的進程在超過期優先被殺死,這也回答了咱們本文開頭提出的問題。
3、根據cachedPrcess和emptyProcess的數量判斷當前內存的緊張程度生成對應等級,根據當前系統內存等級,來要求進程作對應的進程內的回收。