Skip to content

Commit ee7621c

Browse files
author
Dianne Hackborn
committed
Modify how the background process LRU list is handled.
A long time ago, we had a concept of an "empty" process -- this was a process that didn't have any interesting components in it, which would be placed below everything else in the LRU list. Empty processes didn't work out well, because you could get into bad situations where you have filled your LRU list with things that have hidden activities, pushing empty processes to the bottom and being immediately killed as soon as they go into the list. So this was removed. This change brings the concept back, but in a slightly different form, to address a more specific problem: for people who are switching between N different applications, we would like to try to keep those activities available in RAM in a consistent manner. Currently the previous activities would be killed often quickly and suprisingly, even on devices with lots of RAM. This is for two reasons: (1) As you sit in one application, other things going on in the background will go to the top of the LRU list, pushing down the previous apps you have visited, even though you aren't aware at all of these other things executing. (2) There is a hard limit on the number of background processes (currently 16) after which they are killed regardless of the amount of available RAM. This is desireable because if there is lots of RAM we can end up with tons and tons of processes sitting around, not really serving any purpose, but using up resources. To improve the situation, we have again a concept of "empty" processes but now it means one with no activities. Processes that aren't empty but in the background list are called hidden. We maintain these as two parallel lists, each getting half of the process limit: so with a 16 process limit, you can have at most 8 empty and 8 hidden processes. This allows us to consistently keep up to 8 recent applications around for fast app switching; we will also keep around 8 other processes to make it more efficient for background work to execute again if it needs to. Change-Id: Iee06e45efc20787da6a1e50020e5421c28204bd7
1 parent ee8655c commit ee7621c

File tree

2 files changed

+147
-48
lines changed

2 files changed

+147
-48
lines changed

services/java/com/android/server/am/ActivityManagerService.java

Lines changed: 140 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -685,6 +685,18 @@ private class Identity {
685685
*/
686686
int mLruSeq = 0;
687687

688+
/**
689+
* Keep track of the non-hidden/empty process we last found, to help
690+
* determine how to distribute hidden/empty processes next time.
691+
*/
692+
int mNumNonHiddenProcs = 0;
693+
694+
/**
695+
* Keep track of the number of hidden procs, to balance oom adj
696+
* distribution between those and empty procs.
697+
*/
698+
int mNumHiddenProcs = 0;
699+
688700
/**
689701
* Keep track of the number of service processes we last found, to
690702
* determine on the next iteration which should be B services.
@@ -9081,7 +9093,9 @@ boolean dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
90819093
pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep);
90829094
pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity);
90839095
pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq);
9084-
pw.println(" mNumServiceProcs=" + mNumServiceProcs
9096+
pw.println(" mNumNonHiddenProcs=" + mNumNonHiddenProcs
9097+
+ " mNumHiddenProcs=" + mNumHiddenProcs
9098+
+ " mNumServiceProcs=" + mNumServiceProcs
90859099
+ " mNewNumServiceProcs=" + mNewNumServiceProcs);
90869100
}
90879101

@@ -9746,6 +9760,7 @@ public int compare(Pair<ProcessRecord, Integer> object1,
97469760
pw.print(" ");
97479761
pw.print("oom: max="); pw.print(r.maxAdj);
97489762
pw.print(" hidden="); pw.print(r.hiddenAdj);
9763+
pw.print(" empty="); pw.print(r.emptyAdj);
97499764
pw.print(" curRaw="); pw.print(r.curRawAdj);
97509765
pw.print(" setRaw="); pw.print(r.setRawAdj);
97519766
pw.print(" cur="); pw.print(r.curAdj);
@@ -11966,22 +11981,23 @@ private BroadcastQueue isReceivingBroadcast(ProcessRecord app) {
1196611981
}
1196711982

1196811983
private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
11969-
ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
11984+
int emptyAdj, ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
1197011985
if (mAdjSeq == app.adjSeq) {
1197111986
// This adjustment has already been computed. If we are calling
1197211987
// from the top, we may have already computed our adjustment with
1197311988
// an earlier hidden adjustment that isn't really for us... if
1197411989
// so, use the new hidden adjustment.
1197511990
if (!recursed && app.hidden) {
11976-
app.curAdj = app.curRawAdj = app.nonStoppingAdj = hiddenAdj;
11991+
app.curAdj = app.curRawAdj = app.nonStoppingAdj =
11992+
app.hasActivities ? hiddenAdj : emptyAdj;
1197711993
}
1197811994
return app.curRawAdj;
1197911995
}
1198011996

1198111997
if (app.thread == null) {
1198211998
app.adjSeq = mAdjSeq;
1198311999
app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
11984-
return (app.curAdj=ProcessList.HIDDEN_APP_MAX_ADJ);
12000+
return (app.curAdj=app.curRawAdj=ProcessList.HIDDEN_APP_MAX_ADJ);
1198512001
}
1198612002

1198712003
app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
@@ -11998,6 +12014,7 @@ private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
1199812014
app.adjType = "fixed";
1199912015
app.adjSeq = mAdjSeq;
1200012016
app.curRawAdj = app.nonStoppingAdj = app.maxAdj;
12017+
app.hasActivities = false;
1200112018
app.foregroundActivities = false;
1200212019
app.keeping = true;
1200312020
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -12008,12 +12025,15 @@ private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
1200812025
app.systemNoUi = true;
1200912026
if (app == TOP_APP) {
1201012027
app.systemNoUi = false;
12028+
app.hasActivities = true;
1201112029
} else if (activitiesSize > 0) {
1201212030
for (int j = 0; j < activitiesSize; j++) {
1201312031
final ActivityRecord r = app.activities.get(j);
1201412032
if (r.visible) {
1201512033
app.systemNoUi = false;
12016-
break;
12034+
}
12035+
if (r.app == app) {
12036+
app.hasActivities = true;
1201712037
}
1201812038
}
1201912039
}
@@ -12022,6 +12042,7 @@ private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
1202212042

1202312043
app.keeping = false;
1202412044
app.systemNoUi = false;
12045+
app.hasActivities = false;
1202512046

1202612047
// Determine the importance of the process, starting with most
1202712048
// important to least, and assign an appropriate OOM adjustment.
@@ -12037,6 +12058,7 @@ private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
1203712058
app.adjType = "top-activity";
1203812059
foregroundActivities = true;
1203912060
interesting = true;
12061+
app.hasActivities = true;
1204012062
} else if (app.instrumentationClass != null) {
1204112063
// Don't want to kill running instrumentation.
1204212064
adj = ProcessList.FOREGROUND_APP_ADJ;
@@ -12058,21 +12080,13 @@ private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
1205812080
adj = ProcessList.FOREGROUND_APP_ADJ;
1205912081
schedGroup = Process.THREAD_GROUP_DEFAULT;
1206012082
app.adjType = "exec-service";
12061-
} else if (activitiesSize > 0) {
12062-
// This app is in the background with paused activities.
12063-
// We inspect activities to potentially upgrade adjustment further below.
12064-
adj = hiddenAdj;
12065-
schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
12066-
app.hidden = true;
12067-
app.adjType = "bg-activities";
1206812083
} else {
12069-
// A very not-needed process. If this is lower in the lru list,
12070-
// we will push it in to the empty bucket.
12084+
// Assume process is hidden (has activities); we will correct
12085+
// later if this is not the case.
1207112086
adj = hiddenAdj;
1207212087
schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
1207312088
app.hidden = true;
12074-
app.empty = true;
12075-
app.adjType = "bg-empty";
12089+
app.adjType = "bg-activities";
1207612090
}
1207712091

1207812092
boolean hasStoppingActivities = false;
@@ -12089,6 +12103,7 @@ private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
1208912103
}
1209012104
schedGroup = Process.THREAD_GROUP_DEFAULT;
1209112105
app.hidden = false;
12106+
app.hasActivities = true;
1209212107
foregroundActivities = true;
1209312108
break;
1209412109
} else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
@@ -12106,9 +12121,20 @@ private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
1210612121
foregroundActivities = true;
1210712122
hasStoppingActivities = true;
1210812123
}
12124+
if (r.app == app) {
12125+
app.hasActivities = true;
12126+
}
1210912127
}
1211012128
}
1211112129

12130+
if (adj == hiddenAdj && !app.hasActivities) {
12131+
// Whoops, this process is completely empty as far as we know
12132+
// at this point.
12133+
adj = emptyAdj;
12134+
app.empty = true;
12135+
app.adjType = "bg-empty";
12136+
}
12137+
1211212138
if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
1211312139
if (app.foregroundServices) {
1211412140
// The user is aware of this app, so make it visible.
@@ -12242,8 +12268,16 @@ private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
1224212268
myHiddenAdj = ProcessList.VISIBLE_APP_ADJ;
1224312269
}
1224412270
}
12245-
clientAdj = computeOomAdjLocked(
12246-
client, myHiddenAdj, TOP_APP, true, doingAll);
12271+
int myEmptyAdj = emptyAdj;
12272+
if (myEmptyAdj > client.emptyAdj) {
12273+
if (client.emptyAdj >= ProcessList.VISIBLE_APP_ADJ) {
12274+
myEmptyAdj = client.emptyAdj;
12275+
} else {
12276+
myEmptyAdj = ProcessList.VISIBLE_APP_ADJ;
12277+
}
12278+
}
12279+
clientAdj = computeOomAdjLocked(client, myHiddenAdj,
12280+
myEmptyAdj, TOP_APP, true, doingAll);
1224712281
String adjType = null;
1224812282
if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
1224912283
// Not doing bind OOM management, so treat
@@ -12382,8 +12416,16 @@ private final int computeOomAdjLocked(ProcessRecord app, int hiddenAdj,
1238212416
myHiddenAdj = ProcessList.FOREGROUND_APP_ADJ;
1238312417
}
1238412418
}
12385-
int clientAdj = computeOomAdjLocked(
12386-
client, myHiddenAdj, TOP_APP, true, doingAll);
12419+
int myEmptyAdj = emptyAdj;
12420+
if (myEmptyAdj > client.emptyAdj) {
12421+
if (client.emptyAdj > ProcessList.FOREGROUND_APP_ADJ) {
12422+
myEmptyAdj = client.emptyAdj;
12423+
} else {
12424+
myEmptyAdj = ProcessList.FOREGROUND_APP_ADJ;
12425+
}
12426+
}
12427+
int clientAdj = computeOomAdjLocked(client, myHiddenAdj,
12428+
myEmptyAdj, TOP_APP, true, doingAll);
1238712429
if (adj > clientAdj) {
1238812430
if (app.hasShownUi && app != mHomeProcess
1238912431
&& clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -12796,9 +12838,10 @@ final void checkExcessivePowerUsageLocked(boolean doKills) {
1279612838
}
1279712839
}
1279812840

12799-
private final boolean updateOomAdjLocked(
12800-
ProcessRecord app, int hiddenAdj, ProcessRecord TOP_APP, boolean doingAll) {
12841+
private final boolean updateOomAdjLocked(ProcessRecord app, int hiddenAdj,
12842+
int emptyAdj, ProcessRecord TOP_APP, boolean doingAll) {
1280112843
app.hiddenAdj = hiddenAdj;
12844+
app.emptyAdj = emptyAdj;
1280212845

1280312846
if (app.thread == null) {
1280412847
return false;
@@ -12808,7 +12851,7 @@ private final boolean updateOomAdjLocked(
1280812851

1280912852
boolean success = true;
1281012853

12811-
computeOomAdjLocked(app, hiddenAdj, TOP_APP, false, doingAll);
12854+
computeOomAdjLocked(app, hiddenAdj, emptyAdj, TOP_APP, false, doingAll);
1281212855

1281312856
if (app.curRawAdj != app.setRawAdj) {
1281412857
if (wasKeeping && !app.keeping) {
@@ -12895,7 +12938,8 @@ final boolean updateOomAdjLocked(ProcessRecord app) {
1289512938

1289612939
mAdjSeq++;
1289712940

12898-
boolean success = updateOomAdjLocked(app, app.hiddenAdj, TOP_APP, false);
12941+
boolean success = updateOomAdjLocked(app, app.hiddenAdj, app.emptyAdj,
12942+
TOP_APP, false);
1289912943
final boolean nowHidden = app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ
1290012944
&& app.curAdj <= ProcessList.HIDDEN_APP_MAX_ADJ;
1290112945
if (nowHidden != wasHidden) {
@@ -12923,43 +12967,91 @@ final void updateOomAdjLocked() {
1292312967
// how many slots we have for background processes; we may want
1292412968
// to put multiple processes in a slot of there are enough of
1292512969
// them.
12926-
int numSlots = ProcessList.HIDDEN_APP_MAX_ADJ - ProcessList.HIDDEN_APP_MIN_ADJ + 1;
12927-
int factor = (mLruProcesses.size()-4)/numSlots;
12928-
if (factor < 1) factor = 1;
12929-
int step = 0;
12970+
int numSlots = (ProcessList.HIDDEN_APP_MAX_ADJ
12971+
- ProcessList.HIDDEN_APP_MIN_ADJ + 1) / 2;
12972+
int emptyFactor = (mLruProcesses.size()-mNumNonHiddenProcs-mNumHiddenProcs)/numSlots;
12973+
if (emptyFactor < 1) emptyFactor = 1;
12974+
int hiddenFactor = (mNumHiddenProcs > 0 ? mNumHiddenProcs : 1)/numSlots;
12975+
if (hiddenFactor < 1) hiddenFactor = 1;
12976+
int stepHidden = 0;
12977+
int stepEmpty = 0;
12978+
final int emptyProcessLimit = mProcessLimit > 1 ? mProcessLimit / 2 : mProcessLimit;
12979+
final int hiddenProcessLimit = mProcessLimit > 1 ? mProcessLimit / 2 : mProcessLimit;
1293012980
int numHidden = 0;
12981+
int numEmpty = 0;
1293112982
int numTrimming = 0;
12932-
12983+
12984+
mNumNonHiddenProcs = 0;
12985+
mNumHiddenProcs = 0;
12986+
1293312987
// First update the OOM adjustment for each of the
1293412988
// application processes based on their current state.
1293512989
int i = mLruProcesses.size();
1293612990
int curHiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
12991+
int nextHiddenAdj = curHiddenAdj+1;
12992+
int curEmptyAdj = ProcessList.HIDDEN_APP_MIN_ADJ;
12993+
int nextEmptyAdj = curEmptyAdj+2;
1293712994
while (i > 0) {
1293812995
i--;
1293912996
ProcessRecord app = mLruProcesses.get(i);
1294012997
//Slog.i(TAG, "OOM " + app + ": cur hidden=" + curHiddenAdj);
12941-
updateOomAdjLocked(app, curHiddenAdj, TOP_APP, true);
12942-
if (curHiddenAdj < ProcessList.HIDDEN_APP_MAX_ADJ
12943-
&& app.curAdj == curHiddenAdj) {
12944-
step++;
12945-
if (step >= factor) {
12946-
step = 0;
12947-
curHiddenAdj++;
12948-
}
12949-
}
12998+
updateOomAdjLocked(app, curHiddenAdj, curEmptyAdj, TOP_APP, true);
1295012999
if (!app.killedBackground) {
12951-
if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
13000+
if (app.curRawAdj == curHiddenAdj && app.hasActivities) {
13001+
// This process was assigned as a hidden process... step the
13002+
// hidden level.
13003+
mNumHiddenProcs++;
13004+
if (curHiddenAdj != nextHiddenAdj) {
13005+
stepHidden++;
13006+
if (stepHidden >= hiddenFactor) {
13007+
stepHidden = 0;
13008+
curHiddenAdj = nextHiddenAdj;
13009+
nextHiddenAdj += 2;
13010+
if (nextHiddenAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
13011+
nextHiddenAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
13012+
}
13013+
}
13014+
}
1295213015
numHidden++;
12953-
if (numHidden > mProcessLimit) {
13016+
if (numHidden > hiddenProcessLimit) {
1295413017
Slog.i(TAG, "No longer want " + app.processName
1295513018
+ " (pid " + app.pid + "): hidden #" + numHidden);
1295613019
EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
1295713020
app.processName, app.setAdj, "too many background");
1295813021
app.killedBackground = true;
1295913022
Process.killProcessQuiet(app.pid);
1296013023
}
13024+
} else {
13025+
if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curHiddenAdj) {
13026+
// This process was assigned as an empty process... step the
13027+
// empty level.
13028+
if (curEmptyAdj != nextEmptyAdj) {
13029+
stepEmpty++;
13030+
if (stepEmpty >= emptyFactor) {
13031+
stepEmpty = 0;
13032+
curEmptyAdj = nextEmptyAdj;
13033+
nextEmptyAdj += 2;
13034+
if (nextEmptyAdj > ProcessList.HIDDEN_APP_MAX_ADJ) {
13035+
nextEmptyAdj = ProcessList.HIDDEN_APP_MAX_ADJ;
13036+
}
13037+
}
13038+
}
13039+
} else if (app.curRawAdj < ProcessList.HIDDEN_APP_MIN_ADJ) {
13040+
mNumNonHiddenProcs++;
13041+
}
13042+
if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
13043+
numEmpty++;
13044+
if (numEmpty > emptyProcessLimit) {
13045+
Slog.i(TAG, "No longer want " + app.processName
13046+
+ " (pid " + app.pid + "): empty #" + numEmpty);
13047+
EventLog.writeEvent(EventLogTags.AM_KILL, app.pid,
13048+
app.processName, app.setAdj, "too many background");
13049+
app.killedBackground = true;
13050+
Process.killProcessQuiet(app.pid);
13051+
}
13052+
}
1296113053
}
12962-
if (!app.killedBackground && app.isolated && app.services.size() <= 0) {
13054+
if (app.isolated && app.services.size() <= 0) {
1296313055
// If this is an isolated process, and there are no
1296413056
// services running in it, then the process is no longer
1296513057
// needed. We agressively kill these because we can by
@@ -12989,18 +13081,20 @@ final void updateOomAdjLocked() {
1298913081
// are managing to keep around is less than half the maximum we desire;
1299013082
// if we are keeping a good number around, we'll let them use whatever
1299113083
// memory they want.
12992-
if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/2)) {
13084+
if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/4)
13085+
&& numEmpty <= (ProcessList.MAX_HIDDEN_APPS/4)) {
13086+
final int numHiddenAndEmpty = numHidden + numEmpty;
1299313087
final int N = mLruProcesses.size();
12994-
factor = numTrimming/3;
13088+
int factor = numTrimming/3;
1299513089
int minFactor = 2;
1299613090
if (mHomeProcess != null) minFactor++;
1299713091
if (mPreviousProcess != null) minFactor++;
1299813092
if (factor < minFactor) factor = minFactor;
12999-
step = 0;
13093+
int step = 0;
1300013094
int fgTrimLevel;
13001-
if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/5)) {
13095+
if (numHiddenAndEmpty <= (ProcessList.MAX_HIDDEN_APPS/5)) {
1300213096
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
13003-
} else if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/3)) {
13097+
} else if (numHiddenAndEmpty <= (ProcessList.MAX_HIDDEN_APPS/3)) {
1300413098
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
1300513099
} else {
1300613100
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;

0 commit comments

Comments
 (0)