相關名詞:
關鍵名詞:adj、minfree
此關鍵名詞:oom_score、oom_score_adj、oom_adj
內存相關:total、free、used、lost、VSS、RSS、PSS、USS
這裏不講解adj與lmk水位相關基礎知識,重點在:如果不適用google的配置策略,如何使用自定義的水位的方法。
google的策略就是根據實際內存及屏幕尺寸來計算lmk的水位,一般夠用,不過很多廠商都自己配置。
最近看個問題,就是當內存不足(這個不好定義,怎麼叫不足?)的時候,系統可能卡,系統可能一直再殺進程。
什麼時候殺進程,殺哪些進程,和系統剩餘內存的關係是如何關聯的?
這個問題就是adj和lmk了,基礎知識部分需要百度學習下。(主要是adj相關)
一、調試
1.看內存:
adb shell dumpsys meminfo
會打印很多,按順序打印每個進程所佔用的內存,再最尾打印總信息:
total是總內存,free是可用內存,used是使用內存,lost是系統已經使用,但是無法確定哪裏使用的(total-free-used得出)。
2.看內存使用略微詳細及排名:
adb shell 並su(root權限),然後procrank
3.看具體某個進程的內存使用情況:
adb shell ps
之後,看到具體進程的進程號和名字,然後:
以上具體含義,進行百度瞭解。
當內存使用大體情況看到以後,看下lmk的水位及adj值:
看到adj值和lmk水位是一一對應的。
這裏的lmk水位需要換算一下,如26624換算成m單位:26624*4/1024 = 104M,這個是page爲單位(一page一般是4k)的數字,所以到m需要換算。
也就是當剩餘104M內存時,lmk會殺死906級別(adj=906 code中是:static final int CACHED_APP_MAX_ADJ = 906;)的進程來釋放內存。
那麼一部設備,在什麼水位殺哪些類進程,這個是比較難的,可能需要進行嘗試來確定,或者是根據使用場景不通來確定。
這也就是手機中有:節能、省電、遊戲模式的部分原理。在遊戲模式,可能殺死後臺所有的進程來保證遊戲的流暢性,當然還有更多的是cpu、gpu的滿頻等調整。
手動嘗試修改lmk水位:minfree
adb shell並su,然後輸入命令
cat /sys/module/lowmemorykiller/parameters/minfree看看原來的水位,然後調整自己期望調試的水位值:
echo "18432,23040,27648,32256,36864,46080" > /sys/module/lowmemorykiller/parameters/minfree
這種方法,設備重啓後無效;
那麼在嘗試也給ok的水位之後,如何讓他永久生效呢,這裏提供的方法是原聲code開發,貌似市面也有類似工具,可百度下載。
code修改方法:
水位的設定是在ProcessList.java中完成的:
188 private final int[] mOomMinFreeLow = new int[] {
189 12288, 18432, 24576,
190 36864, 43008, 49152
191 };
192 // These are the high-end OOM level limits. This is appropriate for a
193 // 1280x800 or larger screen with around 1GB RAM. Values are in KB.
194 private final int[] mOomMinFreeHigh = new int[] {
195 73728, 92160, 110592,
196 129024, 147456, 184320
197 };
228 private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
229 // Scale buckets from avail memory: at 300MB we use the lowest values to
230 // 700MB or more for the top values.
231 float scaleMem = ((float)(mTotalMemMb-350))/(700-350);
232
233 // Scale buckets from screen size.
234 int minSize = 480*800; // 384000
235 int maxSize = 1280*800; // 1024000 230400 870400 .264
236 float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
237 if (false) {
238 Slog.i("XXXXXX", "scaleMem=" + scaleMem);
239 Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth
240 + " dh=" + displayHeight);
241 }
242
243 float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;
244 if (scale < 0) scale = 0;
245 else if (scale > 1) scale = 1;
246 int minfree_adj = Resources.getSystem().getInteger(
247 com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);
248 int minfree_abs = Resources.getSystem().getInteger(
249 com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);
250 if (false) {
251 Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);
252 }
253
254 final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
255
256 for (int i=0; i<mOomAdj.length; i++) {
257 int low = mOomMinFreeLow[i];
258 int high = mOomMinFreeHigh[i];
259 if (is64bit) {
260 // Increase the high min-free levels for cached processes for 64-bit
261 if (i == 4) high = (high*3)/2;
262 else if (i == 5) high = (high*7)/4;
263 }
264 mOomMinFree[i] = (int)(low + ((high-low)*scale));
265 }
266
267 if (minfree_abs >= 0) {
268 for (int i=0; i<mOomAdj.length; i++) {
269 mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i]
270 / mOomMinFree[mOomAdj.length - 1]);
271 }
272 }
273
274 if (minfree_adj != 0) {
275 for (int i=0; i<mOomAdj.length; i++) {
276 mOomMinFree[i] += (int)((float)minfree_adj * mOomMinFree[i]
277 / mOomMinFree[mOomAdj.length - 1]);
278 if (mOomMinFree[i] < 0) {
279 mOomMinFree[i] = 0;
280 }
281 }
282 }
283
284 // The maximum size we will restore a process from cached to background, when under
285 // memory duress, is 1/3 the size we have reserved for kernel caches and other overhead
286 // before killing background processes.
287 mCachedRestoreLevel = (getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3;
288
289 // Ask the kernel to try to keep enough memory free to allocate 3 full
290 // screen 32bpp buffers without entering direct reclaim.
291 int reserve = displayWidth * displayHeight * 4 * 3 / 1024;
292 int reserve_adj = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);
293 int reserve_abs = Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);
294
295 if (reserve_abs >= 0) {
296 reserve = reserve_abs;
297 }
298
299 if (reserve_adj != 0) {
300 reserve += reserve_adj;
301 if (reserve < 0) {
302 reserve = 0;
303 }
304 }
305
306 if (write) {
307 ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
308 buf.putInt(LMK_TARGET);
309 for (int i=0; i<mOomAdj.length; i++) {
310 buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
311 buf.putInt(mOomAdj[i]);
312 }
313
314 writeLmkd(buf);
315 SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
316 }
317 // GB: 2048,3072,4096,6144,7168,8192
318 // HC: 8192,10240,12288,14336,16384,20480
319 }
根據兩個數組,進行計算得到一組lmk水位。然後寫道lmkd.c中進行使用,在lmkd中根據現有剩餘內存與水位比較來進行進程的生殺。
上面看到有個wirte的參數,這個參數決定是否要通過socket通信寫個lmkd。我們永久生效的方法,就可以在這裏做手腳。
當然也可以修改數組值,不過不推薦,因爲Google給了,你不想用可以,但是別動google的東西,直接上你自己的一套數值就可以了。
我們可以把數字放到txt裏,然後再編譯得時候將txt拷貝到out目錄,這樣就存到手機裏了,然後再讀取文件,把數值存儲下來,就ok了。下面看下方案:
一般的廠商在目錄devices下面的mk中配置txt及拷貝到手機的路徑:
配置lmk水位及對應adj:
1 adj:0,200,400,600,900,906
2 # 32M,40M,48M,88M,100M,104M
3 minfree:8192,10240,12288,22528,25600,26624
txt存放:
拷貝路徑及配置(拷貝到設備的system/etc/目錄下 : 前面是源文件路徑,後面是拷貝目的路徑):
代碼修改如下(把源碼中write爲true時的邏輯改一下),讀取文件,並解析存儲,然後寫到buffer裏。
if (write) {
int[] cfgOomMinFree = null;
int[] cfgOomAdj = null;
String lowmemCfg = "";
// adjust lowmemorykiller config according to ddr size
if (mTotalMemMb > 1024) {
lowmemCfg = "/system/etc/lowmemorykiller_2G.txt";
} else if (mTotalMemMb > 512) {
lowmemCfg = "/system/etc/lowmemorykiller.txt";
} else {
lowmemCfg = "/system/etc/lowmemorykiller_512M.txt";
}
try{
java.io.BufferedReader br = new java.io.BufferedReader(new java.io.InputStreamReader(
new java.io.FileInputStream(lowmemCfg)));
String line = "";
while ((line = br.readLine()) != null) {
if (line.startsWith("#")) {//skip this line
continue;
}
else if (line.startsWith("adj:")) {
String str = line.substring("adj:".length());
if (str.split(",").length == mOomAdj.length) {
cfgOomAdj = new int[mOomAdj.length];
for (int i = 0; i < mOomAdj.length; i++)
cfgOomAdj[i] = Integer.parseInt(str.split(",")[i]);
Slog.i(TAG, "adjConfigString: " + str);
}
}
else if (line.startsWith("minfree:")) {
String str = line.substring("minfree:".length());
if (str.split(",").length == mOomAdj.length) {
cfgOomMinFree = new int[mOomAdj.length];
for (int i = 0; i < mOomAdj.length; i++)
cfgOomMinFree[i] = Integer.parseInt(str.split(",")[i]);
Slog.i(TAG, "minfreeConfigString: " + str);
}
}
}
br.close();
} catch (java.io.FileNotFoundException ex) {
} catch (java.io.IOException ex) {
}
ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
buf.putInt(LMK_TARGET);
for (int i=0; i<mOomAdj.length; i++) {
if ((null != cfgOomMinFree) && (null != cfgOomAdj)) {
buf.putInt(cfgOomMinFree[i]);
buf.putInt(cfgOomAdj[i]);
//update mOomMinFree and mOomAdj array
mOomMinFree[i] = cfgOomMinFree[i] * 4; // turn to kB
mOomAdj[i] = cfgOomAdj[i];
} else {
buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
buf.putInt(mOomAdj[i]);
}
}