12.3 Low Memory Killer机制
Android借鉴Linux进程管理机制,提供了自己的实现LM Killer(Low Memory Killer)。与Linux的OOM Killer(Out Of Memory Killer)机制类似,它们都是在内存不足时,根据一定的策略选择“杀死”一部分进程。通常可选的策略为OOM adj,系统根据进程的adj值选择哪些进程可以被“杀死”。
LM Killer与OOM Killer的不同之处在于:OOM Killer只有在内存不足时才选择“杀死”OOM adj值较高的进程;而LM Killer将OOM adj划分为不同的等级,并为每个等级指定了最小剩余内存阈值,当剩余内存小于该阈值时便可以“杀死”该等级内的进程。因此LM Killer比OOM Killer更灵活。
应用程序退出后,系统通常将其切换到后台,并不会“杀死”应用程序进程,这样加快了下次启动该应用程序的速度,只有当内存不足或者低于某个阈值时,才会回收应用程序进程。LM Killer相对于直接“杀死”进程是一种被动的进程管理机制。
12.3.1 OOM adj等级和最小内存阈值
LM Killer是Android为Linux新增的驱动程序,其源码位于kernel/drivers/staging/android/lowmemorykiller.c中,其中OOM adj等级由/sys/module/lowmemorykiller/parameters/adj文件指定,最小内存阈值由/sys/module/lowmemorykiller/parameters/minfree文件指定,这两个文件在init.rc文件中有如下配置:
Memory management.Basic kernel parameters, and allow the high
level system server to be able to adjust the kernel OOM driver
parameters to match how it is managing things.
write/proc/sys/vm/overcommit_memory 1
write/proc/sys/vm/min_free_order_shift 4
只有root和system用户可以操作这两个文件
chown root system/sys/module/lowmemorykiller/parameters/adj
chmod 0664/sys/module/lowmemorykiller/parameters/adj
chown root system/sys/module/lowmemorykiller/parameters/minfree
chmod 0664/sys/module/lowmemorykiller/parameters/minfree
……
on early-init
为init进程设置OOM adj值为-16
write/proc/1/oom_adj-16
start ueventd
OOM adj的取值范围是-17~+15,值越大越容易被“杀死”。其中-17用于禁止进程在发生OOM时被“杀死”。-16是init进程的OOM adj值,这个值仅次于-17,但仍然可能被“杀死”,只是优先级最低。
上述两个文件可以通过ProcessList提供的接口予以修改。创建ActivityManagerService时,其成员变量mProcessList初始化为ProcessList类型的对象,ActivityManagerService便可以通过mProcessList管理系统的OOM adj等级和每个等级的最小内存阈值。ProcessList的构造函数如下:
ProcessList(){
//从/proc/meminfo文件读取内存信息,获取MemTotal、MemFree、Cached的大小
MemInfoReader minfo=new MemInfoReader();
minfo.readMemInfo();
//将MemTotal转化为MB
mTotalMemMb=minfo.getTotalSize()/(1024*1024);
updateOomLevels(0,0,false);
}
ProcessList在构造方法中调用了updateOomLevels方法,代码如下:
class ProcessList{
……
static final int PAGE_SIZE=4*1024;
……
//mOomAdj数组中指定了6个OOM adj等级,依次为[0,1,2,4,9,15]
private final int[]mOomAdj=new int[]{
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
BACKUP_APP_ADJ, HIDDEN_APP_MIN_ADJ, HIDDEN_APP_MAX_ADJ
};
……
/*低端设备上对mOomAdj中定义的6种adj级别依次指定OOM最小内存阈值。
低端设备通常指HVGA或者分辨率更小的设备并且内存小于512MB的设备/
private final long[]mOomMinFreeLow=new long[]{
8192,12288,16384,//单位为KB
24576,28672,32768
};
/*高端设备上对mOomAdj中定义的6种adj依次指定OOM最小内存阈值。高端
设备通常指分辨率为1280x800或更大,同时内存超过1GB的设备/
private final long[]mOomMinFreeHigh=new long[]{
32768,40960,49152,//单位为KB
57344,65536,81920
};
//设备对mOomAdj中定义的6种adj实际指定的OOM最小内存阈值
private final long[]mOomMinFree=new long[mOomAdj.length];
private final long mTotalMemMb;//可用内存大小
private void updateOomLevels(int displayWidth, int displayHeight,
boolean write){
//根据可用内存计算出一个比例系数
float scaleMem=((float)(mTotalMemMb-300))/(700-300);
//屏幕尺寸
int minSize=320*480;//通常对应HVGA等低端设备,值为153600
int maxSize=1280*800;//通常对应高端设备,值为1024000
//根据屏幕尺寸计算出一个比例系数
float scaleDisp=((float)(displayWidth*displayHeight)-minSize)
/(maxSize-minSize);
StringBuilder adjString=new StringBuilder();
StringBuilder memString=new StringBuilder();
//比较内存和屏幕的比例系数,计算各个OOM adj等级对应的最小内存阈值
float scale=scaleMem>scaleDisp?scaleMem:scaleDisp;
if(scale<0)scale=0;
else if(scale>1)scale=1;
for(int i=0;i<mOomAdj.length;i++){
long low=mOomMinFreeLow[i];
long high=mOomMinFreeHigh[i];
//根据比例系数计算出实际的最小内存阈值
mOomMinFree[i]=(long)(low+((high-low)*scale));
if(i>0){
adjString.append(',');//不同OOM adj等级以“,”分割
memString.append(',');//不同最小内存阈值以“,”分割
}
adjString.append(mOomAdj[i]);//OOM adj等级
memString.append((mOomMinFree[i]*1024)/PAGE_SIZE);//最小内存阈值
}
if(write){//本例为false
writeFile("/sys/module/lowmemorykiller/parameters/adj",
adjString.toString());
writeFile("/sys/module/lowmemorykiller/parameters/minfree",
memString.toString());
}
//GB(Gingerbread):2048,3072,4096,6144,7168,8192
//HC(Honeycomb):8192,10240,12288,14336,16384,20480
}
updateOomLevels方法的主要工作是修改两个系统文件,进而更新系统的OOM adj等级和最小内存阈值。最终,/sys/module/lowmemorykiller/parameters/adj文件的内容如下:
0,1,2,4,9,15
/sys/module/lowmemorykiller/parameters/minfree文件的内容与Android版本有关,假设其内容如下:
2048,3072,4096,6144,7168,8192//单位为page,默认1 page=4KB
以上内容说明对于OOM adj等级为0的进程,当系统内存低于2048×4/1024=8MB时,才会回收该等级的进程;对于OOM adj等级为15的进程,当系统内存低于8192×4/1024=32MB时,就会回收该等级的进程。