相关名词:
关键名词: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相关)
adb shell dumpsys meminfo
会打印很多,按顺序打印每个进程所占用的内存,再最尾打印总信息:
total是总内存,free是可用内存,used是使用内存,lost是系统已经使用,但是无法确定哪里使用的(total-free-used得出)。
adb shell 并su(root权限),然后procrank
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的满频等调整。
adb shell并su,然后输入命令
cat /sys/module/lowmemorykiller/parameters/minfree看看原来的水位,然后调整自己期望调试的水位值:
echo "18432,23040,27648,32256,36864,46080" > /sys/module/lowmemorykiller/parameters/minfree
这种方法,设备重启后无效;
那么在尝试也给ok的水位之后,如何让他永久生效呢,这里提供的方法是原声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= 0) {
268 for (int i=0; i= 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
根据两个数组,进行计算得到一组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