OomAdjuster: use oom_adj_score tiers for cached apps am: 326c51d79b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21515935

Change-Id: Ic294798577dd96c42103c2209599974e08c9f757
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Tim Murray
2023-02-23 23:01:07 +00:00
committed by Automerger Merge Worker
3 changed files with 188 additions and 119 deletions

View File

@@ -146,6 +146,9 @@ final class ActivityManagerConstants extends ContentObserver {
*/
static final String KEY_NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms";
static final String KEY_USE_TIERED_CACHED_ADJ = "use_tiered_cached_adj";
static final String KEY_TIERED_CACHED_ADJ_DECAY_TIME = "tiered_cached_adj_decay_time";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
@@ -201,6 +204,9 @@ final class ActivityManagerConstants extends ContentObserver {
static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000;
private static final boolean DEFAULT_USE_TIERED_CACHED_ADJ = false;
private static final long DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME = 60 * 1000;
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
*/
@@ -1011,6 +1017,12 @@ final class ActivityManagerConstants extends ContentObserver {
public volatile long mShortFgsAnrExtraWaitDuration =
DEFAULT_SHORT_FGS_ANR_EXTRA_WAIT_DURATION;
/** @see #KEY_USE_TIERED_CACHED_ADJ */
public boolean USE_TIERED_CACHED_ADJ = DEFAULT_USE_TIERED_CACHED_ADJ;
/** @see #KEY_TIERED_CACHED_ADJ_DECAY_TIME */
public long TIERED_CACHED_ADJ_DECAY_TIME = DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME;
private final OnPropertiesChangedListener mOnDeviceConfigChangedListener =
new OnPropertiesChangedListener() {
@Override
@@ -1179,6 +1191,10 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_MAX_PREVIOUS_TIME:
updateMaxPreviousTime();
break;
case KEY_USE_TIERED_CACHED_ADJ:
case KEY_TIERED_CACHED_ADJ_DECAY_TIME:
updateUseTieredCachedAdj();
break;
default:
break;
}
@@ -1924,6 +1940,17 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION);
}
private void updateUseTieredCachedAdj() {
USE_TIERED_CACHED_ADJ = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_USE_TIERED_CACHED_ADJ,
DEFAULT_USE_TIERED_CACHED_ADJ);
TIERED_CACHED_ADJ_DECAY_TIME = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_TIERED_CACHED_ADJ_DECAY_TIME,
DEFAULT_TIERED_CACHED_ADJ_DECAY_TIME);
}
@NeverCompile // Avoid size overhead of debugging code.
void dump(PrintWriter pw) {
pw.println("ACTIVITY MANAGER SETTINGS (dumpsys activity settings) "
@@ -2108,6 +2135,11 @@ final class ActivityManagerConstants extends ContentObserver {
pw.print(" "); pw.print(KEY_SHORT_FGS_ANR_EXTRA_WAIT_DURATION);
pw.print("="); pw.println(mShortFgsAnrExtraWaitDuration);
pw.print(" "); pw.print(KEY_USE_TIERED_CACHED_ADJ);
pw.print("="); pw.println(USE_TIERED_CACHED_ADJ);
pw.print(" "); pw.print(KEY_TIERED_CACHED_ADJ_DECAY_TIME);
pw.print("="); pw.println(TIERED_CACHED_ADJ_DECAY_TIME);
pw.println();
if (mOverrideMaxCachedProcesses >= 0) {
pw.print(" mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses);

View File

@@ -1073,135 +1073,165 @@ public class OomAdjuster {
@GuardedBy({"mService", "mProcLock"})
private void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) {
final int numLru = lruList.size();
if (mConstants.USE_TIERED_CACHED_ADJ) {
final long now = SystemClock.uptimeMillis();
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
>= UNKNOWN_ADJ) {
final ProcessServiceRecord psr = app.mServices;
int targetAdj = CACHED_APP_MIN_ADJ;
// First update the OOM adjustment for each of the
// application processes based on their current state.
int curCachedAdj = CACHED_APP_MIN_ADJ;
int nextCachedAdj = curCachedAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
int curCachedImpAdj = 0;
int curEmptyAdj = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS;
int nextEmptyAdj = curEmptyAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
if (opt != null && opt.isFreezeExempt()) {
// BIND_WAIVE_PRIORITY and the like get oom_adj 900
targetAdj += 0;
} else if ((state.getSetAdj() >= CACHED_APP_MIN_ADJ)
&& (state.getLastStateTime()
+ mConstants.TIERED_CACHED_ADJ_DECAY_TIME) < now) {
// Older cached apps get 950
targetAdj += 50;
} else {
// Newer cached apps get 910
targetAdj += 10;
}
state.setCurRawAdj(targetAdj);
state.setCurAdj(psr.modifyRawOomAdj(targetAdj));
}
}
} else {
// First update the OOM adjustment for each of the
// application processes based on their current state.
int curCachedAdj = CACHED_APP_MIN_ADJ;
int nextCachedAdj = curCachedAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
int curCachedImpAdj = 0;
int curEmptyAdj = CACHED_APP_MIN_ADJ + CACHED_APP_IMPORTANCE_LEVELS;
int nextEmptyAdj = curEmptyAdj + (CACHED_APP_IMPORTANCE_LEVELS * 2);
final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES
- 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 numEmptyProcs = numLru - 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 cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + mNumSlots - 1) : 1)
/ mNumSlots;
if (cachedFactor < 1) cachedFactor = 1;
final int emptyProcessLimit = mConstants.CUR_MAX_EMPTY_PROCESSES;
final int cachedProcessLimit = mConstants.CUR_MAX_CACHED_PROCESSES
- 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 numEmptyProcs = numLru - 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 cachedFactor = (mNumCachedHiddenProcs > 0
? (mNumCachedHiddenProcs + mNumSlots - 1) : 1)
/ mNumSlots;
if (cachedFactor < 1) cachedFactor = 1;
int emptyFactor = (numEmptyProcs + mNumSlots - 1) / mNumSlots;
if (emptyFactor < 1) emptyFactor = 1;
int emptyFactor = (numEmptyProcs + mNumSlots - 1) / mNumSlots;
if (emptyFactor < 1) emptyFactor = 1;
int stepCached = -1;
int stepEmpty = -1;
int lastCachedGroup = 0;
int lastCachedGroupImportance = 0;
int lastCachedGroupUid = 0;
int stepCached = -1;
int stepEmpty = -1;
int lastCachedGroup = 0;
int lastCachedGroupImportance = 0;
int lastCachedGroupUid = 0;
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
// If we haven't yet assigned the final cached adj
// to the process, do that now.
if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
// If we haven't yet assigned the final cached adj
// to the process, do that now.
if (!app.isKilledByAm() && app.getThread() != null && state.getCurAdj()
>= UNKNOWN_ADJ) {
final ProcessServiceRecord psr = app.mServices;
switch (state.getCurProcState()) {
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.
boolean inGroup = false;
final int connectionGroup = psr.getConnectionGroup();
if (connectionGroup != 0) {
final int connectionImportance = psr.getConnectionImportance();
if (lastCachedGroupUid == app.uid
final ProcessServiceRecord psr = app.mServices;
switch (state.getCurProcState()) {
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.
boolean inGroup = false;
final int connectionGroup = psr.getConnectionGroup();
if (connectionGroup != 0) {
final int connectionImportance = psr.getConnectionImportance();
if (lastCachedGroupUid == app.uid
&& lastCachedGroup == connectionGroup) {
// This is in the same group as the last process, just tweak
// adjustment by importance.
if (connectionImportance > lastCachedGroupImportance) {
lastCachedGroupImportance = connectionImportance;
if (curCachedAdj < nextCachedAdj
// This is in the same group as the last process, just tweak
// adjustment by importance.
if (connectionImportance > lastCachedGroupImportance) {
lastCachedGroupImportance = connectionImportance;
if (curCachedAdj < nextCachedAdj
&& curCachedAdj < CACHED_APP_MAX_ADJ) {
curCachedImpAdj++;
curCachedImpAdj++;
}
}
inGroup = true;
} else {
lastCachedGroupUid = app.uid;
lastCachedGroup = connectionGroup;
lastCachedGroupImportance = connectionImportance;
}
}
if (!inGroup && curCachedAdj != nextCachedAdj) {
stepCached++;
curCachedImpAdj = 0;
if (stepCached >= cachedFactor) {
stepCached = 0;
curCachedAdj = nextCachedAdj;
nextCachedAdj += CACHED_APP_IMPORTANCE_LEVELS * 2;
if (nextCachedAdj > CACHED_APP_MAX_ADJ) {
nextCachedAdj = CACHED_APP_MAX_ADJ;
}
}
inGroup = true;
} else {
lastCachedGroupUid = app.uid;
lastCachedGroup = connectionGroup;
lastCachedGroupImportance = connectionImportance;
}
}
if (!inGroup && curCachedAdj != nextCachedAdj) {
stepCached++;
curCachedImpAdj = 0;
if (stepCached >= cachedFactor) {
stepCached = 0;
curCachedAdj = nextCachedAdj;
nextCachedAdj += CACHED_APP_IMPORTANCE_LEVELS * 2;
if (nextCachedAdj > CACHED_APP_MAX_ADJ) {
nextCachedAdj = CACHED_APP_MAX_ADJ;
// This process is a cached process holding activities...
// assign it the next cached value for that type, and then
// step that cached level.
state.setCurRawAdj(curCachedAdj + curCachedImpAdj);
state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj));
if (DEBUG_LRU) {
Slog.d(TAG_LRU, "Assigning activity LRU #" + i
+ " adj: " + state.getCurAdj()
+ " (curCachedAdj=" + curCachedAdj
+ " curCachedImpAdj=" + curCachedImpAdj + ")");
}
break;
default:
// Figure out the next cached level.
if (curEmptyAdj != nextEmptyAdj) {
stepEmpty++;
if (stepEmpty >= emptyFactor) {
stepEmpty = 0;
curEmptyAdj = nextEmptyAdj;
nextEmptyAdj += CACHED_APP_IMPORTANCE_LEVELS * 2;
if (nextEmptyAdj > CACHED_APP_MAX_ADJ) {
nextEmptyAdj = CACHED_APP_MAX_ADJ;
}
}
}
}
// This process is a cached process holding activities...
// assign it the next cached value for that type, and then
// step that cached level.
state.setCurRawAdj(curCachedAdj + curCachedImpAdj);
state.setCurAdj(psr.modifyRawOomAdj(curCachedAdj + curCachedImpAdj));
if (DEBUG_LRU) {
Slog.d(TAG_LRU, "Assigning activity LRU #" + i
+ " adj: " + state.getCurAdj()
+ " (curCachedAdj=" + curCachedAdj
+ " curCachedImpAdj=" + curCachedImpAdj + ")");
}
break;
default:
// Figure out the next cached level.
if (curEmptyAdj != nextEmptyAdj) {
stepEmpty++;
if (stepEmpty >= emptyFactor) {
stepEmpty = 0;
curEmptyAdj = nextEmptyAdj;
nextEmptyAdj += CACHED_APP_IMPORTANCE_LEVELS * 2;
if (nextEmptyAdj > CACHED_APP_MAX_ADJ) {
nextEmptyAdj = CACHED_APP_MAX_ADJ;
}
// 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.
state.setCurRawAdj(curEmptyAdj);
state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj));
if (DEBUG_LRU) {
Slog.d(TAG_LRU, "Assigning empty LRU #" + i
+ " adj: " + state.getCurAdj()
+ " (curEmptyAdj=" + curEmptyAdj
+ ")");
}
}
// 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.
state.setCurRawAdj(curEmptyAdj);
state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj));
if (DEBUG_LRU) {
Slog.d(TAG_LRU, "Assigning empty LRU #" + i
+ " adj: " + state.getCurAdj() + " (curEmptyAdj=" + curEmptyAdj
+ ")");
}
break;
break;
}
}
}
}
}
private long mNextNoKillDebugMessageTime;
private double mLastFreeSwapPercent = 1.00;

View File

@@ -143,8 +143,8 @@ public class MockingOomAdjusterTests {
private static final String MOCKAPP5_PROCESSNAME = "test #5";
private static final String MOCKAPP5_PACKAGENAME = "com.android.test.test5";
private static final int MOCKAPP2_UID_OTHER = MOCKAPP2_UID + UserHandle.PER_USER_RANGE;
private static final int FIRST_CACHED_ADJ = ProcessList.CACHED_APP_MIN_ADJ
+ ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ
+ ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
private static Context sContext;
private static PackageManagerInternal sPackageManagerInternal;
private static ActivityManagerService sService;
@@ -208,6 +208,9 @@ public class MockingOomAdjusterTests {
new ActiveUids(sService, false));
sService.mOomAdjuster.mAdjSeq = 10000;
sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
if (sService.mConstants.USE_TIERED_CACHED_ADJ) {
sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10;
}
}
@AfterClass
@@ -834,7 +837,7 @@ public class MockingOomAdjusterTests {
updateOomAdj(client, app);
doReturn(null).when(sService).getTopApp();
assertProcStates(app, PROCESS_STATE_SERVICE, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND);
assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -882,7 +885,7 @@ public class MockingOomAdjusterTests {
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -1286,7 +1289,7 @@ public class MockingOomAdjusterTests {
bindProvider(app, app, null, null, false);
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -1301,7 +1304,7 @@ public class MockingOomAdjusterTests {
sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app, client);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FIRST_CACHED_ADJ, SCHED_GROUP_BACKGROUND);
assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND);
}
@SuppressWarnings("GuardedBy")
@@ -2378,8 +2381,12 @@ public class MockingOomAdjusterTests {
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
final int userOwner = 0;
final int userOther = 1;
final int cachedAdj1 = CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
final int cachedAdj2 = cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
final int cachedAdj1 = sService.mConstants.USE_TIERED_CACHED_ADJ
? CACHED_APP_MIN_ADJ + 10
: CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
final int cachedAdj2 = sService.mConstants.USE_TIERED_CACHED_ADJ
? CACHED_APP_MIN_ADJ + 10
: cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2;
doReturn(userOwner).when(sService.mUserController).getCurrentUserId();
final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();