AMS: Proactive Kills - Minimal Implementation

Under certain scenarios, PSI triggers may not wake up LMKD in time to
relieve system resource contention. This is especially true in the case
of MGLRU which makes smarter reclaim decisions and genereates less PSI.

Additionally, reclaim of anonymous memory suffers when swap is full and
under pressure the anon pages can be reactivated in an attempt to swap.
This causes hot/cold page inversion. MGLRU detects this situation and
doesn't swap but it leads to a similar scenario where the anon generations
are in effect inverted (hot/cold generation inversion) if the swap
contention doesn't decrease.

To safe guard these cases, impose a minimum free swap threshold. The
default is 10% but will be configurable from experiment flags. When swap
drops below this limit, the LRU cached app is killed to help relieve
swap pressure. Most cached apps contribute to swap usage because of the
proactive app compaction, which may not necessarily mean we have
memory pressure.

Note that Andorid uses ZRAM (in-memory compressed swap). ZRAM size is
virtual, so imposing a threshold on swap is not the same as reserving
swap space.

Bug: 249601646
Test: Run ACT, observe low swap kills
Change-Id: I089d37fc067b9a85aabbd70b4513a209dfafa96d
Signed-off-by: Kalesh Singh <kaleshsingh@google.com>
This commit is contained in:
Kalesh Singh
2022-11-10 16:00:00 -08:00
parent 0f461bf427
commit 84d812ffb4
2 changed files with 30 additions and 1 deletions

View File

@@ -845,7 +845,7 @@ public final class CachedAppOptimizer {
/**
* Retrieves the free swap percentage.
*/
static private native double getFreeSwapPercent();
static native double getFreeSwapPercent();
/**
* Retrieves the total used physical ZRAM

View File

@@ -1118,6 +1118,12 @@ public class OomAdjuster {
private long mNextNoKillDebugMessageTime;
private double mLastFreeSwapPercent = 1.00;
private static double getFreeSwapPercent() {
return CachedAppOptimizer.getFreeSwapPercent();
}
@GuardedBy({"mService", "mProcLock"})
private boolean updateAndTrimProcessLSP(final long now, final long nowElapsed,
final long oldTime, final ActiveUids activeUids, @OomAdjReason int oomAdjReason) {
@@ -1142,6 +1148,11 @@ public class OomAdjuster {
int numEmpty = 0;
int numTrimming = 0;
boolean proactiveKillsEnabled = false; // TODO: Configure from phenotype
double lowSwapThresholdPercent = 0.10; // TODO: Configure from phenotype
double freeSwapPercent = proactiveKillsEnabled ? getFreeSwapPercent() : 1.00;
ProcessRecord lruCachedApp = null;
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
@@ -1179,6 +1190,8 @@ public class OomAdjuster {
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED,
true);
} else if (proactiveKillsEnabled) {
lruCachedApp = app;
}
break;
case PROCESS_STATE_CACHED_EMPTY:
@@ -1198,6 +1211,8 @@ public class OomAdjuster {
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY,
true);
} else if (proactiveKillsEnabled) {
lruCachedApp = app;
}
}
break;
@@ -1229,6 +1244,20 @@ public class OomAdjuster {
}
}
if (proactiveKillsEnabled // Proactive kills enabled?
&& doKillExcessiveProcesses // Should kill excessive processes?
&& freeSwapPercent < lowSwapThresholdPercent // Swap below threshold?
&& lruCachedApp != null // If no cached app, let LMKD decide
// If swap is non-decreasing, give reclaim a chance to catch up
&& freeSwapPercent < mLastFreeSwapPercent) {
lruCachedApp.killLocked("swap low and too many cached",
ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TOO_MANY_CACHED,
true);
}
mLastFreeSwapPercent = freeSwapPercent;
return mService.mAppProfiler.updateLowMemStateLSP(numCached, numEmpty, numTrimming);
}