• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.am;
18 
19 import android.provider.DeviceConfig;
20 import android.util.Slog;
21 
22 import com.android.internal.annotations.GuardedBy;
23 import com.android.internal.annotations.VisibleForTesting;
24 
25 import java.io.PrintWriter;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Comparator;
29 import java.util.concurrent.Executor;
30 
31 /**
32  * Class to re-rank a number of the least recently used processes before they
33  * are assigned oom adjust scores.
34  */
35 public class CacheOomRanker {
36     @VisibleForTesting
37     static final String KEY_USE_OOM_RE_RANKING = "use_oom_re_ranking";
38     private static final boolean DEFAULT_USE_OOM_RE_RANKING = false;
39     @VisibleForTesting
40     static final String KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK = "oom_re_ranking_number_to_re_rank";
41     @VisibleForTesting static final int DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK = 8;
42     @VisibleForTesting
43     static final String KEY_OOM_RE_RANKING_LRU_WEIGHT = "oom_re_ranking_lru_weight";
44     @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_LRU_WEIGHT = 0.35f;
45     @VisibleForTesting
46     static final String KEY_OOM_RE_RANKING_USES_WEIGHT = "oom_re_ranking_uses_weight";
47     @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_USES_WEIGHT = 0.5f;
48     @VisibleForTesting
49     static final String KEY_OOM_RE_RANKING_RSS_WEIGHT = "oom_re_ranking_rss_weight";
50     @VisibleForTesting static final float DEFAULT_OOM_RE_RANKING_RSS_WEIGHT = 0.15f;
51 
52     private static final Comparator<RankedProcessRecord> SCORED_PROCESS_RECORD_COMPARATOR =
53             new ScoreComparator();
54     private static final Comparator<RankedProcessRecord> CACHE_USE_COMPARATOR =
55             new CacheUseComparator();
56     private static final Comparator<RankedProcessRecord> LAST_RSS_COMPARATOR =
57             new LastRssComparator();
58     private static final Comparator<RankedProcessRecord> LAST_ACTIVITY_TIME_COMPARATOR =
59             new LastActivityTimeComparator();
60 
61     private final Object mPhenotypeFlagLock = new Object();
62 
63     private final ActivityManagerService mService;
64     private final ActivityManagerGlobalLock mProcLock;
65     private final Object mProfilerLock;
66 
67     @GuardedBy("mPhenotypeFlagLock")
68     private boolean mUseOomReRanking = DEFAULT_USE_OOM_RE_RANKING;
69     // Weight to apply to the LRU ordering.
70     @GuardedBy("mPhenotypeFlagLock")
71     @VisibleForTesting float mLruWeight = DEFAULT_OOM_RE_RANKING_LRU_WEIGHT;
72     // Weight to apply to the ordering by number of times the process has been added to the cache.
73     @GuardedBy("mPhenotypeFlagLock")
74     @VisibleForTesting float mUsesWeight = DEFAULT_OOM_RE_RANKING_USES_WEIGHT;
75     // Weight to apply to the ordering by RSS used by the processes.
76     @GuardedBy("mPhenotypeFlagLock")
77     @VisibleForTesting float mRssWeight = DEFAULT_OOM_RE_RANKING_RSS_WEIGHT;
78 
79     // Positions to replace in the lru list.
80     @GuardedBy("mPhenotypeFlagLock")
81     private int[] mLruPositions;
82     // Processes to re-rank
83     @GuardedBy("mPhenotypeFlagLock")
84     private RankedProcessRecord[] mScoredProcessRecords;
85 
86     private final DeviceConfig.OnPropertiesChangedListener mOnFlagsChangedListener =
87             new DeviceConfig.OnPropertiesChangedListener() {
88                 @Override
89                 public void onPropertiesChanged(DeviceConfig.Properties properties) {
90                     synchronized (mPhenotypeFlagLock) {
91                         for (String name : properties.getKeyset()) {
92                             if (KEY_USE_OOM_RE_RANKING.equals(name)) {
93                                 updateUseOomReranking();
94                             } else if (KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK.equals(name)) {
95                                 updateNumberToReRank();
96                             } else if (KEY_OOM_RE_RANKING_LRU_WEIGHT.equals(name)) {
97                                 updateLruWeight();
98                             } else if (KEY_OOM_RE_RANKING_USES_WEIGHT.equals(name)) {
99                                 updateUsesWeight();
100                             } else if (KEY_OOM_RE_RANKING_RSS_WEIGHT.equals(name)) {
101                                 updateRssWeight();
102                             }
103                         }
104                     }
105                 }
106             };
107 
CacheOomRanker(final ActivityManagerService service)108     CacheOomRanker(final ActivityManagerService service) {
109         mService = service;
110         mProcLock = service.mProcLock;
111         mProfilerLock = service.mAppProfiler.mProfilerLock;
112     }
113 
114     /** Load settings from device config and register a listener for changes. */
init(Executor executor)115     public void init(Executor executor) {
116         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
117                 executor, mOnFlagsChangedListener);
118         synchronized (mPhenotypeFlagLock) {
119             updateUseOomReranking();
120             updateNumberToReRank();
121             updateLruWeight();
122             updateUsesWeight();
123             updateRssWeight();
124         }
125     }
126 
127     /**
128      * Returns whether oom re-ranking is enabled.
129      */
useOomReranking()130     public boolean useOomReranking() {
131         synchronized (mPhenotypeFlagLock) {
132             return mUseOomReRanking;
133         }
134     }
135 
136     @GuardedBy("mPhenotypeFlagLock")
updateUseOomReranking()137     private void updateUseOomReranking() {
138         mUseOomReRanking = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
139                 KEY_USE_OOM_RE_RANKING, DEFAULT_USE_OOM_RE_RANKING);
140     }
141 
142     @GuardedBy("mPhenotypeFlagLock")
updateNumberToReRank()143     private void updateNumberToReRank() {
144         int previousNumberToReRank = getNumberToReRank();
145         int numberToReRank = DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
146                 KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK, DEFAULT_OOM_RE_RANKING_NUMBER_TO_RE_RANK);
147         if (previousNumberToReRank != numberToReRank) {
148             mScoredProcessRecords = new RankedProcessRecord[numberToReRank];
149             for (int i = 0; i < mScoredProcessRecords.length; ++i) {
150                 mScoredProcessRecords[i] = new RankedProcessRecord();
151             }
152             mLruPositions = new int[numberToReRank];
153         }
154     }
155 
156     @GuardedBy("mPhenotypeFlagLock")
157     @VisibleForTesting
getNumberToReRank()158     int getNumberToReRank() {
159         return mScoredProcessRecords == null ? 0 : mScoredProcessRecords.length;
160     }
161 
162     @GuardedBy("mPhenotypeFlagLock")
updateLruWeight()163     private void updateLruWeight() {
164         mLruWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
165                 KEY_OOM_RE_RANKING_LRU_WEIGHT, DEFAULT_OOM_RE_RANKING_LRU_WEIGHT);
166     }
167 
168     @GuardedBy("mPhenotypeFlagLock")
updateUsesWeight()169     private void updateUsesWeight() {
170         mUsesWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
171                 KEY_OOM_RE_RANKING_USES_WEIGHT, DEFAULT_OOM_RE_RANKING_USES_WEIGHT);
172     }
173 
174     @GuardedBy("mPhenotypeFlagLock")
updateRssWeight()175     private void updateRssWeight() {
176         mRssWeight = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
177                 KEY_OOM_RE_RANKING_RSS_WEIGHT, DEFAULT_OOM_RE_RANKING_RSS_WEIGHT);
178     }
179 
180     /**
181      * Re-rank the cached processes in the lru list with a weighted ordering
182      * of lru, rss size and number of times the process has been put in the cache.
183      */
184     @GuardedBy({"mService", "mProcLock"})
reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart)185     void reRankLruCachedAppsLSP(ArrayList<ProcessRecord> lruList, int lruProcessServiceStart) {
186         float lruWeight;
187         float usesWeight;
188         float rssWeight;
189         int[] lruPositions;
190         RankedProcessRecord[] scoredProcessRecords;
191 
192         synchronized (mPhenotypeFlagLock) {
193             lruWeight = mLruWeight;
194             usesWeight = mUsesWeight;
195             rssWeight = mRssWeight;
196             lruPositions = mLruPositions;
197             scoredProcessRecords = mScoredProcessRecords;
198         }
199 
200         // Don't re-rank if the class hasn't been initialized with defaults.
201         if (lruPositions == null || scoredProcessRecords == null) {
202             return;
203         }
204 
205         // Collect the least recently used processes to re-rank, only rank cached
206         // processes further down the list than mLruProcessServiceStart.
207         int cachedProcessPos = 0;
208         for (int i = 0; i < lruProcessServiceStart
209                 && cachedProcessPos < scoredProcessRecords.length; ++i) {
210             ProcessRecord app = lruList.get(i);
211             // Processes that will be assigned a cached oom adj score.
212             if (!app.isKilledByAm() && app.getThread() != null && app.mState.getCurAdj()
213                     >= ProcessList.UNKNOWN_ADJ) {
214                 scoredProcessRecords[cachedProcessPos].proc = app;
215                 scoredProcessRecords[cachedProcessPos].score = 0.0f;
216                 lruPositions[cachedProcessPos] = i;
217                 ++cachedProcessPos;
218             }
219         }
220 
221         // TODO maybe ensure a certain number above this in the cache before re-ranking.
222         if (cachedProcessPos < scoredProcessRecords.length)  {
223             // Ignore we don't have enough processes to worry about re-ranking.
224             return;
225         }
226 
227         // Add scores for each of the weighted features we want to rank based on.
228         if (lruWeight > 0.0f) {
229             // This doesn't use the LRU list ordering as after the first re-ranking
230             // that will no longer be lru.
231             Arrays.sort(scoredProcessRecords, LAST_ACTIVITY_TIME_COMPARATOR);
232             addToScore(scoredProcessRecords, lruWeight);
233         }
234         if (rssWeight > 0.0f) {
235             synchronized (mService.mAppProfiler.mProfilerLock) {
236                 Arrays.sort(scoredProcessRecords, LAST_RSS_COMPARATOR);
237             }
238             addToScore(scoredProcessRecords, rssWeight);
239         }
240         if (usesWeight > 0.0f) {
241             Arrays.sort(scoredProcessRecords, CACHE_USE_COMPARATOR);
242             addToScore(scoredProcessRecords, usesWeight);
243         }
244 
245         // Re-rank by the new combined score.
246         Arrays.sort(scoredProcessRecords, SCORED_PROCESS_RECORD_COMPARATOR);
247 
248         if (ActivityManagerDebugConfig.DEBUG_OOM_ADJ) {
249             boolean printedHeader = false;
250             for (int i = 0; i < scoredProcessRecords.length; ++i) {
251                 if (scoredProcessRecords[i].proc.getPid()
252                         != lruList.get(lruPositions[i]).getPid()) {
253                     if (!printedHeader) {
254                         Slog.i(OomAdjuster.TAG, "reRankLruCachedApps");
255                         printedHeader = true;
256                     }
257                     Slog.i(OomAdjuster.TAG, "  newPos=" + lruPositions[i] + " "
258                             + scoredProcessRecords[i].proc);
259                 }
260             }
261         }
262 
263         for (int i = 0; i < scoredProcessRecords.length; ++i) {
264             lruList.set(lruPositions[i], scoredProcessRecords[i].proc);
265             scoredProcessRecords[i].proc = null;
266         }
267     }
268 
addToScore(RankedProcessRecord[] scores, float weight)269     private static void addToScore(RankedProcessRecord[] scores, float weight) {
270         for (int i = 1; i < scores.length; ++i) {
271             scores[i].score += i * weight;
272         }
273     }
274 
dump(PrintWriter pw)275     void dump(PrintWriter pw) {
276         pw.println("CacheOomRanker settings");
277         synchronized (mPhenotypeFlagLock) {
278             pw.println("  " + KEY_USE_OOM_RE_RANKING + "=" + mUseOomReRanking);
279             pw.println("  " + KEY_OOM_RE_RANKING_NUMBER_TO_RE_RANK + "=" + getNumberToReRank());
280             pw.println("  " + KEY_OOM_RE_RANKING_LRU_WEIGHT + "=" + mLruWeight);
281             pw.println("  " + KEY_OOM_RE_RANKING_USES_WEIGHT + "=" + mUsesWeight);
282             pw.println("  " + KEY_OOM_RE_RANKING_RSS_WEIGHT + "=" + mRssWeight);
283         }
284     }
285 
286     private static class ScoreComparator implements Comparator<RankedProcessRecord> {
287         @Override
compare(RankedProcessRecord o1, RankedProcessRecord o2)288         public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
289             return Float.compare(o1.score, o2.score);
290         }
291     }
292 
293     private static class LastActivityTimeComparator implements Comparator<RankedProcessRecord> {
294         @Override
compare(RankedProcessRecord o1, RankedProcessRecord o2)295         public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
296             return Long.compare(o1.proc.getLastActivityTime(), o2.proc.getLastActivityTime());
297         }
298     }
299 
300     private static class CacheUseComparator implements Comparator<RankedProcessRecord> {
301         @Override
compare(RankedProcessRecord o1, RankedProcessRecord o2)302         public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
303             return Long.compare(o1.proc.mState.getCacheOomRankerUseCount(),
304                     o2.proc.mState.getCacheOomRankerUseCount());
305         }
306     }
307 
308     private static class LastRssComparator implements Comparator<RankedProcessRecord> {
309         @Override
compare(RankedProcessRecord o1, RankedProcessRecord o2)310         public int compare(RankedProcessRecord o1, RankedProcessRecord o2) {
311             // High RSS first to match least recently used.
312             return Long.compare(o2.proc.mProfile.getLastRss(), o1.proc.mProfile.getLastRss());
313         }
314     }
315 
316     private static class RankedProcessRecord {
317         public ProcessRecord proc;
318         public float score;
319     }
320 }
321