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

扫描二维码关注公众号,回复: 2514447 查看本文章

会打印很多,按顺序打印每个进程所占用的内存,再最尾打印总信息:

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]);
                  }
             }

猜你喜欢

转载自blog.csdn.net/shi_xin/article/details/81171687
今日推荐