Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f07755e6 authored by Tim Murray's avatar Tim Murray
Browse files

OomAdjuster: use oom_adj_score tiers for cached apps

Metrics suggest that cached apps do not get promoted out of cached in
anything resembling LRU ordering. Maintaining the LRU order is
expensive for both ActivityManager and LMKD. Instead of a strict LRU,
use tiers of oom_adj_scores for cached apps:

- 900 for cached apps bound with BIND_WAIVE_PRIORITY or similar
- 910 for freezer-eligible apps that entered cached in the last 60s
- 950 for freezer-eligible apps that entered cached more than 60s ago

Tiered oom_adj_scores for cached apps can be enabled with the
"use_tiered_cached_adj" DeviceConfig flag, and the decay time (in ms)
can be modified with the "tiered_cached_adj_decay_time" DeviceConfig
flag.

oom_adj_score tiers currently default to off.

Bug: 253908413
Test: atest MockingOomAdjusterTests

Change-Id: Ie386672c321227eb689902863df0b0022fa09e49
parent 2cdd45e2
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
@@ -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}
     */
@@ -989,6 +995,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
@@ -1151,6 +1163,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;
                        }
@@ -1881,6 +1897,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) "
@@ -2058,6 +2085,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);
+145 −115
Original line number Diff line number Diff line
@@ -1074,7 +1074,34 @@ 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;

                    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;
@@ -1100,7 +1127,8 @@ public class OomAdjuster {
                // instead of a gazillion empty processes.
                numEmptyProcs = cachedProcessLimit;
            }
        int cachedFactor = (mNumCachedHiddenProcs > 0 ? (mNumCachedHiddenProcs + mNumSlots - 1) : 1)
            int cachedFactor = (mNumCachedHiddenProcs > 0
                    ? (mNumCachedHiddenProcs + mNumSlots - 1) : 1)
                               / mNumSlots;
            if (cachedFactor < 1) cachedFactor = 1;

@@ -1113,6 +1141,7 @@ public class OomAdjuster {
            int lastCachedGroupImportance = 0;
            int lastCachedGroupUid = 0;


            for (int i = numLru - 1; i >= 0; i--) {
                ProcessRecord app = lruList.get(i);
                final ProcessStateRecord state = app.mState;
@@ -1194,7 +1223,8 @@ public class OomAdjuster {
                            state.setCurAdj(psr.modifyRawOomAdj(curEmptyAdj));
                            if (DEBUG_LRU) {
                                Slog.d(TAG_LRU, "Assigning empty LRU #" + i
                                    + " adj: " + state.getCurAdj() + " (curEmptyAdj=" + curEmptyAdj
                                        + " adj: " + state.getCurAdj()
                                        + " (curEmptyAdj=" + curEmptyAdj
                                        + ")");
                            }
                            break;
@@ -1202,7 +1232,7 @@ public class OomAdjuster {
                }
            }
        }

    }
    private long mNextNoKillDebugMessageTime;

    private double mLastFreeSwapPercent = 1.00;
+15 −8
Original line number Diff line number Diff line
@@ -143,7 +143,7 @@ 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
    private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ
                                          + ProcessList.CACHED_APP_IMPORTANCE_LEVELS;
    private static Context sContext;
    private static PackageManagerInternal sPackageManagerInternal;
@@ -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();