Android LowMemoryKiller相關

相關名詞:

關鍵名詞: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]);
                  }
             }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章