一.简要说明
二.源码分析
frameworks/base/services/core/java/com/android/server/am/OomAdjuster.java,入口是assignCachedAdjIfNecessary
,updateAndTrimProcessLSP
方法
@GuardedBy({"mService", "mProcLock"})private void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {final int numLru = lruList.size();// cacheAdj值默认起点是900int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;// 那么下一个cache进程,则在当前进程的adj基础上 + (10),例如:900 + (5 * 2) = 910// 所以cache进程的标志为:910,920,930,940等int nextCachedAdj = curCachedAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);int curCachedImpAdj = 0;// emptyADj值起点为:900 + 5 = 905int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;// 那么下一个empty进程,则在当前进程的adj基础上 + (10),例如:905 + (5 * 2) = 915// 所以分辨empty可以通过:905,915,925,935,945来分辨int nextEmptyAdj = curEmptyAdj + (ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2);// empty进程存在的上限:cache进程的上限CUR_MAX_CACHED_PROCESSES(32)/2 = 16个final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;// cache进程存在的上限:CUR_MAX_CACHED_PROCESSES(32)个 - emptyProcessLimit(16) = 16个final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES- emptyProcessLimit;// 所以cache进程数量上限是32个 - emptyProcessLimit的数量// emptyProcessLimit又是cache数量的一半// empty进程数量 = empty总数量 - 非cache - cache隐藏int numEmptyProcs = numLru - mNumNonCachedProcs - mNumCachedHiddenProcs;...int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + mNumSlots - 1) : 1)/ mNumSlots;int emptyFactor = (numEmptyProcs + mNumSlots - 1) / mNumSlots;// 遍历所有empty和cache进程for (int i = numLru - 1; i >= 0; i--) {ProcessRecord app = lruList.get(i);final ProcessStateRecord state = app.mState;// 如果进程adj值确实是 >=UNKNOWN_ADJ,说明是empty或cacheif (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()>= ProcessList.UNKNOWN_ADJ) {final ProcessServiceRecord psr = app.mServices;switch (state.getCurProcState()) {// 如果是cache进程,计算curCachedImpAdj的数量case PROCESS_STATE_CACHED_ACTIVITY:case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:case ActivityManager.PROCESS_STATE_CACHED_RECENT:// Figure out the next cached level, taking into account groups....// 根据cache因子计算下一个cache进程的adj值if (!inGroup && curCachedAdj != nextCachedAdj) {stepCached++;curCachedImpAdj = 0;if (stepCached >= cachedFactor) {stepCached = 0;curCachedAdj = nextCachedAdj;**nextCachedAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;**// 最大不能超过999if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;}}}// 设置cache adj值state.setCurRawAdj(curCachedAdj + curCachedImpAdj);state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj));break;default:// Figure out the next cached level.// 计算empty值if (curEmptyAdj != nextEmptyAdj) {stepEmpty++;if (stepEmpty >= emptyFactor) {stepEmpty = 0;curEmptyAdj = nextEmptyAdj;// 计算下一次empty值nextEmptyAdj += ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;}}}state.setCurRawAdj(curEmptyAdj);state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj));break;}}}}
所以以上方法的主要作用就是处理empty和cache进程的adj值
- empty的起点是905,每次进程的间隔是10,例如915,925,935
- cache的起点是900,每次进程的间隔是10,例如910,920,930
然后约束其不能超过999最大值,然后继续设置下一次Empty/Cache的Adj值间隔+=10(因为这是遍历所有cache/Empty进程),然后设置adj值即可,所以总结一下:设置empty和cache进程adj值起点,防止每个进程间隔太近导致同时被kill,造成卡顿等情况,循环遍历每个进程,每个进程必须间隔10,且用步进为5来区分是cache还是empty,并约束不能超过999的最大adj值,然后调用setCurRawAdj和setCurAdj设置最终值即可
继续看updateAndTrimProcessLSP
方法,该方法用于kill这些进程
@GuardedBy({"mService", "mProcLock"})private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed,final long oldTime, final ActiveUids activeUids) {// 获取所有进程ArrayList<ProcessRecord> lruList = mProcessList.getLruProcessesLOSP();final int numLru = lruList.size();// 获取empty和cache进程数量上限,已经讲过了// emptyProcessLimit:32 / 2 = 16// cachedProcessLimit:32 - emptyProcessLimit = 16final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES- emptyProcessLimit;int lastCachedGroup = 0;int lastCachedGroupUid = 0;int numCached = 0;int numCachedExtraGroup = 0;int numEmpty = 0;int numTrimming = 0;// 遍历所有进程for (int i = numLru - 1; i >= 0; i--) {ProcessRecord app = lruList.get(i);final ProcessStateRecord state = app.mState;// 要求进程存在,即ActivityThread存在if (!app.isKilledByAm() && app.getThread() != null) {// We don't need to apply the update for the process which didn't get computedif (state.getCompletedAdjSeq() == mAdjSeq) {applyOomAdjLSP(app, true, now, nowElapsed);}// 获取该进程的所有服务final ProcessServiceRecord psr = app.mServices;// Count the number of process types.switch (state.getCurProcState()) {// 如果进程状态是cachedcase PROCESS_STATE_CACHED_ACTIVITY:case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:mNumCachedHiddenProcs++;// cached进程数量++numCached++;final int connectionGroup = psr.getConnectionGroup();if (connectionGroup != 0) {if (lastCachedGroupUid == app.info.uid&& lastCachedGroup == connectionGroup) {// If this process is the next in the same group, we don't// want it to count against our limit of the number of cached// processes, so bump up the group count to account for it.// 如果这个进程是和上个进程属于同一个组内,我们不希望它计入我们缓存进程数量的限制numCachedExtraGroup++;} else {lastCachedGroupUid = app.info.uid;lastCachedGroup = connectionGroup;}} else {lastCachedGroupUid = lastCachedGroup = 0;}// 如果cached进程数量 > 16个则进行主动kill// ,要排除numCachedExtraGroup(因为目前的进程和上一个进程属于同一个组))if ((numCached - numCachedExtraGroup) > cachedProcessLimit) {app.killLocked("cached #" + numCached,ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED,true);}break;case PROCESS_STATE_CACHED_EMPTY:// 如果是empty进程// 如果empty进程数量 > (MAX_CACHED_PROCESSES(32) / 2) / 2 = 8并且该进程最近活跃时间在30以内,则进行主动kill// 一般empty只允许存活30分钟,但如果超过了8个也主动kill,所以empty进程超过30分钟,但在8个以内则不会被杀if (numEmpty > mConstants.CUR_TRIM_EMPTY_PROCESSES&& app.getLastActivityTime() < oldTime) {app.killLocked("empty for " + ((oldTime + ProcessList.MAX_EMPTY_TIME- app.getLastActivityTime()) / 1000) + "s",ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_TRIM_EMPTY,true);} else {// 会先执行else来计算empty进程数量numEmpty++;// 如果进程数量超过16个,不解释,直接killif (numEmpty > emptyProcessLimit) {app.killLocked("empty #" + numEmpty,ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY,true);}}break;default:mNumNonCachedProcs++;break;}// 如果是孤立进程且没有服务在运行,则也直接killif (app.isolated && psr.numberOfRunningServices() <= 0&& app.getIsolatedEntryPoint() == null) {// If this is an isolated process, there are no services// running in it, and it's not a special process with a// custom entry point, 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.killLocked("isolated not needed", ApplicationExitInfo.REASON_OTHER,ApplicationExitInfo.SUBREASON_ISOLATED_NOT_NEEDED, true);} else {// Keeping this process, update its uid.updateAppUidRecLSP(app);}// 大于>home基本上都是cache或者empty进程if (state.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME&& !app.isKilledByAm()) {// 会记录这种进程的次数,此时还不做处理numTrimming++;}}}mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(activeUids);// 传递cached进程数量,empty数量,numTrimming数量,决定内存处于哪个阶段等级return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming);}
所以以内逻辑可以分为:
- 获取cached进程和empty进程的limit数量限制,都为16个
- 遍历所有进程,如果发现上一个进程和该进程是同一个组内,则排除这种进程被kill(只排除cached进程类别)
- 首先处理cached进程,如果cached进程数量超过了16个则直接kill
- 其次处理empty进程,如果数量超过16个则直接kill,如果进程数量没超过16个,但是进程活跃时间超过30分钟并且进程数量大于了8个,也会被kill—也就是empty进程数量超过8个,并且当前进程30分钟内没有活跃会被kill
- 然后进入
updateLowMemStateLSP
调整内存等级
三.内存水平决策分析
frameworks/base/services/core/java/com/android/server/am/AppProfiler.java,主要分析updateLowMemStateLSP
@GuardedBy({"mService", "mProcLock"})boolean updateLowMemStateLSP(int numCached/*cached数量*/, int numEmpty/*empty数量*/, int numTrimming/*进程>=PROCESS_STATE_HOME的数量*/) {final long now = SystemClock.uptimeMillis();// 内存因子int memFactor;// 如果kernel支持mLowMemDetector功能,才会有mLowMemDetector对象存在// 如果mLowMemDetector存在,默认内存因子为ADJ_MEM_FACTOR_NORMAL(0)if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {memFactor = mLowMemDetector.getMemFactor();} else {// 讨论此类情况// 如果cached进程数量 <= (MAX_CACHED_PROCESSES(32) - 16) / 3 = 5个(因为是int)// 并且empty数量 <= 16 / 2 = 8个才会去调整内存因子,内存因子越大,内存压力越大if (numCached <= mService.mConstants.CUR_TRIM_CACHED_PROCESSES&& numEmpty <= mService.mConstants.CUR_TRIM_EMPTY_PROCESSES) {// 计算empty+cached总进程数量final int numCachedAndEmpty = numCached + numEmpty;// 如果总数量 <= 3---微调临界阈值,则内存因子为3if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {memFactor = ADJ_MEM_FACTOR_CRITICAL;// 如果总数量 <= 5,则赋予5,所以数量在3~5个,则内存因子为2} else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {memFactor = ADJ_MEM_FACTOR_LOW;} else {// 其他情况,为1memFactor = ADJ_MEM_FACTOR_MODERATE;}} else {// 如果进程数量大于了规定的5 + 8个,则赋予0memFactor = ADJ_MEM_FACTOR_NORMAL;}}...// 记录当前的内存因子为最新调整的一次mLastMemoryLevel = memFactor;mLastNumProcesses = mService.mProcessList.getLruSizeLOSP();boolean allChanged;int trackerMemFactor;synchronized (mService.mProcessStats.mLock) {// 将内存因子保存到ProcessStats中或ServiceState中allChanged = mService.mProcessStats.setMemFactorLocked(memFactor,mService.mAtmInternal == null || !mService.mAtmInternal.isSleeping(), now);// 获取上次的内存因子trackerMemFactor = mService.mProcessStats.getMemFactorLocked();}// 如果内存因子不属于正常情况,进入低内存状态if (memFactor != ADJ_MEM_FACTOR_NORMAL) {if (mLowRamStartTime == 0) {mLowRamStartTime = now;}// 内存水平int fgTrimLevel;// 通过内存因子,得到内存TrimLevel等级switch (memFactor) {// 如果内存因子是3,则fgTrimLevel为15case ADJ_MEM_FACTOR_CRITICAL:// 您正在运行的进程应该释放尽可能多的非关键资源,以便将内存用于其他地方fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;break;case ADJ_MEM_FACTOR_LOW: // 如果内存因子为2,则fgTrimLevel为10// 您正在运行的进程应该释放不需要的资源,以便将内存用于其他地方。fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;break;default:// 您正在运行的进程可能希望释放一些不需要的资源,以便在其他地方使用,fgTrimLevel为5fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;break;}...// 最大内存等级,80final int[] curLevel = {ComponentCallbacks2.TRIM_MEMORY_COMPLETE};// 遍历所有进程mService.mProcessList.forEachLruProcessesLOSP(true, app -> {final ProcessProfileRecord profile = app.mProfile;// 获取该进程的内存水平final int trimMemoryLevel = profile.getTrimMemoryLevel();final ProcessStateRecord state = app.mState;// 获取进程状态final int curProcState = state.getCurProcState();IApplicationThread thread;// 如果进程状态>=home,代表进程状态不是很好if (curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.isKilledByAm()) {...// 默认设置最大内存水平thread.scheduleTrimMemory(curLevel[0])} else if (curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT&& !app.isKilledByAm()) {...thread.scheduleTrimMemory(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);profile.setTrimMemoryLevel(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);} else {if ((curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND|| state.isSystemNoUi()) && profile.hasPendingUiClean()) {thread.scheduleTrimMemory(level);}if (trimMemoryLevel < fgTrimLevel && (thread = app.getThread()) != null) {try {if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName+ " to " + fgTrimLevel);}thread.scheduleTrimMemory(fgTrimLevel);} catch (RemoteException e) {}}profile.setTrimMemoryLevel(fgTrimLevel);}});} else {// 内存水平正常的情况,代表内存不紧张的情况mService.mProcessList.forEachLruProcessesLOSP(true, app -> {final ProcessProfileRecord profile = app.mProfile;final IApplicationThread thread;final ProcessStateRecord state = app.mState;if (allChanged || state.hasProcStateChanged()) {mService.setProcessTrackerStateLOSP(app, trackerMemFactor, now);state.setProcStateChanged(false);}if ((state.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND|| state.isSystemNoUi()) && profile.hasPendingUiClean()) {thread.scheduleTrimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);}profile.setTrimMemoryLevel(0);});}return allChanged;}
以上就是根据cached进程数量和empty进程数量来确定内存因子,再由内存因子决定内存水平(fgTrimLevel),然后再根据进程状态调用setTrimMemoryLevel
和scheduleTrimMemory
设置不同的内存水平。
当前的cachedProcess和emptyProcess进程的数量来综合判定当前进程的等内存级,这两类进程的数量越少, 表示系统内存越紧张,内存等级越高。
lowmemeorykiller的机制又会在系统可用内存不足时杀死这些进程, 所以在后台进程和空进程数量少于一定数量时,便表示了系统以及触发了lowmemrorykiller的机制,即表示当前系统内存的紧张程度
总结:后台进程与空进程越多表示内存越富裕,越少表示内存越紧张,这些内存level会传递给app,如果activity重写了OnTrimMemory
,则会将当前level传递给activity,开发者可以根据level情况来释放资源,然后内存level也会传递给WindowManagerGlobal#trimMemory
,如果内存level≥60,则销毁与已知窗口关联的所有surface和layer,以及资源也会被销毁,但进程可能仍然存在,不过再次进入该进程,需要重新加载资源了
三.参数介绍
内存因子参数
- public static final int ADJ_MEM_FACTOR_NORMAL = 0;表示正常的内存等级,内存不紧张,通常empty和cached进程数量较多的情况下会是正常
- public static final int ADJ_MEM_FACTOR_MODERATE = 1;表示存在6~13个empty&&cached进程,内存紧张水平不算太高—不是很紧张
- public static final int ADJ_MEM_FACTOR_LOW = 2;表示存在4~5个empty&&cached进程,属于低内存状态了
- public static final int ADJ_MEM_FACTOR_CRITICAL = 3;表示存在1~3个empty&&cached进程,也属于低内存状态
内存因子对应的内存level介绍
- static final int TRIM_MEMORY_COMPLETE = 80;最大内存水平,代表内存非常紧张,表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的最边缘位置,系统会最优先考虑杀掉我们的应用程序,在这个时候应当尽可能地把一切可以释放的东西都进行释放**,如果进程状态≥Home,会设置这个状态**
- static final int TRIM_MEMORY_MODERATE = 60;表示手机目前内存已经很低了,并且我们的程序处于LRU缓存列表的中间位置,如果手机内存还得不到进一步释放的话,那么我们的程序就有被系统杀掉的风险。如果进程状态≥Home,会设置这个状态
- static final int TRIM_MEMORY_BACKGROUND = 40;表示手机目前内存已经很低了,系统准备开始根据LRU缓存来清理进程。这个时候我们的程序在LRU缓存列表的最近位置,是不太可能被清理掉的,但这时去释放掉一些比较容易恢复的资源能够让手机的内存变得比较充足,从而让我们的程序更长时间地保留在缓存当中,这样当用户返回我们的程序时会感觉非常顺畅,而不是经历了一次重新启动的过程。如果进程状态≥Home,会设置这个状态并且进程状态为PROCESS_STATE_HEAVY_WEIGHT的时候会设置
- static final int TRIM_MEMORY_UI_HIDDEN = 20;表示应用程序的所有UI界面被隐藏了,即用户点击了Home键或者Back键导致应用的UI界面不可见.这时候应该释放一些资源。如果进程状态≥PROCESS_STATE_IMPORTANT_BACKGROUND,会设置这个水平
- static final int TRIM_MEMORY_RUNNING_CRITICAL = 15;表示应用程序仍然正常运行,但是系统已经根据LRU缓存规则杀掉了部分缓存的进程了。这个时候我们应当尽可能地去释放任何无用的资源,不然的话系统可能会继续杀掉所有缓存中的进程,并且开始杀掉一些本来应当保持运行的进程,比如说后台运行的服务,该如果内存因子为
ADJ_MEM_FACTOR_CRITICAL
,会设置这个水平 - static final int TRIM_MEMORY_RUNNING_LOW = 10;表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经非常低了,我们应该去释放掉一些不必要的资源以提升系统的性能,同时这也会直接影响到我们应用程序的性能。该如果内存因子为*
ADJ_MEM_FACTOR_LOW
*,会设置这个水平 - static final int TRIM_MEMORY_RUNNING_MODERATE = 5;表示应用程序正常运行,并且不会被杀掉。但是目前手机的内存已经有点低了,系统可能会开始根据LRU缓存规则来去杀死进程了,该水平对应了内存因子的:default,只要不是
ADJ_MEM_FACTOR_CRITICAL
和ADJ_MEM_FACTOR_LOW
,默认内存因子对应的水平都是这个