Compact persistent and BFGS processes.

Persistent and BFGS processes are large contributors to memory consumption,
but they often need a much smaller working set than they have allocated.
Compact them preemptively to prevent LMKs.

Test: boots, works
bug 125346716
bug 119988524

Change-Id: I20af99e1932c262272388e99b79d12e8f10a1e07
This commit is contained in:
Tim Murray
2019-02-19 14:29:39 -08:00
parent 53cc8bf12c
commit bf3f369eb1
2 changed files with 119 additions and 27 deletions

View File

@@ -51,6 +51,8 @@ public final class AppCompactor {
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2";
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3";
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4";
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_5 = "compact_throttle_5";
@VisibleForTesting static final String KEY_COMPACT_THROTTLE_6 = "compact_throttle_6";
@VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE =
"compact_statsd_sample_rate";
@@ -73,6 +75,8 @@ public final class AppCompactor {
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_4 = 10_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_5 = 10 * 60 * 1000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_6 = 10 * 60 * 1000;
// The sampling rate to push app compaction events into statsd for upload.
@VisibleForTesting static final float DEFAULT_STATSD_SAMPLE_RATE = 0.1f;
@@ -85,6 +89,8 @@ public final class AppCompactor {
// Handler constants.
static final int COMPACT_PROCESS_SOME = 1;
static final int COMPACT_PROCESS_FULL = 2;
static final int COMPACT_PROCESS_PERSISTENT = 3;
static final int COMPACT_PROCESS_BFGS = 4;
static final int COMPACT_PROCESS_MSG = 1;
/**
@@ -142,6 +148,10 @@ public final class AppCompactor {
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
@GuardedBy("mPhenotypeFlagLock")
private volatile boolean mUseCompaction = DEFAULT_USE_COMPACTION;
private final Random mRandom = new Random();
@@ -224,6 +234,36 @@ public final class AppCompactor {
}
@GuardedBy("mAm")
void compactAppPersistent(ProcessRecord app) {
app.reqCompactAction = COMPACT_PROCESS_PERSISTENT;
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
mCompactionHandler.obtainMessage(
COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
}
@GuardedBy("mAm")
boolean shouldCompactPersistent(ProcessRecord app, long now) {
return (app.lastCompactTime == 0
|| (now - app.lastCompactTime) > mCompactThrottlePersistent);
}
@GuardedBy("mAm")
void compactAppBfgs(ProcessRecord app) {
app.reqCompactAction = COMPACT_PROCESS_BFGS;
mPendingCompactionProcesses.add(app);
mCompactionHandler.sendMessage(
mCompactionHandler.obtainMessage(
COMPACT_PROCESS_MSG, app.curAdj, app.setProcState));
}
@GuardedBy("mAm")
boolean shouldCompactBFGS(ProcessRecord app, long now) {
return (app.lastCompactTime == 0
|| (now - app.lastCompactTime) > mCompactThrottleBFGS);
}
/**
* Reads the flag value from DeviceConfig to determine whether app compaction
* should be enabled, and starts/stops the compaction thread as needed.
@@ -265,10 +305,18 @@ public final class AppCompactor {
String throttleFullFullFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_4);
String throttleBFGSFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_5);
String throttlePersistentFlag =
DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPACT_THROTTLE_6);
if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag)
|| TextUtils.isEmpty(throttleFullSomeFlag)
|| TextUtils.isEmpty(throttleFullFullFlag)) {
|| TextUtils.isEmpty(throttleFullFullFlag)
|| TextUtils.isEmpty(throttleBFGSFlag)
|| TextUtils.isEmpty(throttlePersistentFlag)) {
// Set defaults for all if any are not set.
useThrottleDefaults = true;
} else {
@@ -277,6 +325,8 @@ public final class AppCompactor {
mCompactThrottleSomeFull = Integer.parseInt(throttleSomeFullFlag);
mCompactThrottleFullSome = Integer.parseInt(throttleFullSomeFlag);
mCompactThrottleFullFull = Integer.parseInt(throttleFullFullFlag);
mCompactThrottleBFGS = Integer.parseInt(throttleBFGSFlag);
mCompactThrottlePersistent = Integer.parseInt(throttlePersistentFlag);
} catch (NumberFormatException e) {
useThrottleDefaults = true;
}
@@ -287,6 +337,8 @@ public final class AppCompactor {
mCompactThrottleSomeFull = DEFAULT_COMPACT_THROTTLE_2;
mCompactThrottleFullSome = DEFAULT_COMPACT_THROTTLE_3;
mCompactThrottleFullFull = DEFAULT_COMPACT_THROTTLE_4;
mCompactThrottleBFGS = DEFAULT_COMPACT_THROTTLE_5;
mCompactThrottlePersistent = DEFAULT_COMPACT_THROTTLE_6;
}
}
@@ -332,14 +384,19 @@ public final class AppCompactor {
synchronized (mAm) {
proc = mPendingCompactionProcesses.remove(0);
pendingAction = proc.reqCompactAction;
// don't compact if the process has returned to perceptible
if (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
// and this is only a cached/home/prev compaction
if ((pendingAction == COMPACT_PROCESS_SOME
|| pendingAction == COMPACT_PROCESS_FULL)
&& (proc.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ)) {
return;
}
pid = proc.pid;
name = proc.processName;
pendingAction = proc.reqCompactAction;
lastCompactAction = proc.lastCompactAction;
lastCompactTime = proc.lastCompactTime;
}
@@ -356,31 +413,49 @@ public final class AppCompactor {
// Note that we explicitly don't take mPhenotypeFlagLock here as the flags
// should very seldom change, and taking the risk of using the wrong action is
// preferable to taking the lock for every single compaction action.
if (pendingAction == COMPACT_PROCESS_SOME) {
if ((lastCompactAction == COMPACT_PROCESS_SOME
&& (start - lastCompactTime < mCompactThrottleSomeSome))
|| (lastCompactAction == COMPACT_PROCESS_FULL
&& (start - lastCompactTime
< mCompactThrottleSomeFull))) {
return;
}
} else {
if ((lastCompactAction == COMPACT_PROCESS_SOME
&& (start - lastCompactTime < mCompactThrottleFullSome))
|| (lastCompactAction == COMPACT_PROCESS_FULL
&& (start - lastCompactTime
< mCompactThrottleFullFull))) {
return;
if (lastCompactTime != 0) {
if (pendingAction == COMPACT_PROCESS_SOME) {
if ((lastCompactAction == COMPACT_PROCESS_SOME
&& (start - lastCompactTime < mCompactThrottleSomeSome))
|| (lastCompactAction == COMPACT_PROCESS_FULL
&& (start - lastCompactTime
< mCompactThrottleSomeFull))) {
return;
}
} else if (pendingAction == COMPACT_PROCESS_FULL) {
if ((lastCompactAction == COMPACT_PROCESS_SOME
&& (start - lastCompactTime < mCompactThrottleFullSome))
|| (lastCompactAction == COMPACT_PROCESS_FULL
&& (start - lastCompactTime
< mCompactThrottleFullFull))) {
return;
}
} else if (pendingAction == COMPACT_PROCESS_PERSISTENT) {
if (start - lastCompactTime < mCompactThrottlePersistent) {
return;
}
} else if (pendingAction == COMPACT_PROCESS_BFGS) {
if (start - lastCompactTime < mCompactThrottleBFGS) {
return;
}
}
}
if (pendingAction == COMPACT_PROCESS_SOME) {
action = mCompactActionSome;
} else {
action = mCompactActionFull;
switch (pendingAction) {
case COMPACT_PROCESS_SOME:
action = mCompactActionSome;
break;
// For the time being, treat these as equivalent.
case COMPACT_PROCESS_FULL:
case COMPACT_PROCESS_PERSISTENT:
case COMPACT_PROCESS_BFGS:
action = mCompactActionFull;
break;
default:
action = COMPACT_ACTION_NONE;
break;
}
if (action.equals(COMPACT_ACTION_NONE)) {
if (COMPACT_ACTION_NONE.equals(action)) {
return;
}

View File

@@ -1686,9 +1686,10 @@ public final class OomAdjuster {
int changes = 0;
if (app.curAdj != app.setAdj) {
// don't compact during bootup
if (mAppCompact.useCompaction() && mService.mBooted) {
// don't compact during bootup
if (mAppCompact.useCompaction() && mService.mBooted) {
// Cached and prev/home compaction
if (app.curAdj != app.setAdj) {
// Perform a minor compaction when a perceptible app becomes the prev/home app
// Perform a major compaction when any app enters cached
// reminder: here, setAdj is previous state, curAdj is upcoming state
@@ -1702,7 +1703,23 @@ public final class OomAdjuster {
&& app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ) {
mAppCompact.compactAppFull(app);
}
} else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
&& app.setAdj < ProcessList.FOREGROUND_APP_ADJ
// Because these can fire independent of oom_adj/procstate changes, we need
// to throttle the actual dispatch of these requests in addition to the
// processing of the requests. As a result, there is throttling both here
// and in AppCompactor.
&& mAppCompact.shouldCompactPersistent(app, now)) {
mAppCompact.compactAppPersistent(app);
} else if (mService.mWakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE
&& app.getCurProcState()
== ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
&& mAppCompact.shouldCompactBFGS(app, now)) {
mAppCompact.compactAppBfgs(app);
}
}
if (app.curAdj != app.setAdj) {
ProcessList.setOomAdj(app.pid, app.uid, app.curAdj);
if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) {
String msg = "Set " + app.pid + " " + app.processName + " adj "