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时,就会回收该等级的进程。