1.1 基本原理
Low Memory Killer与OOM的区别:OOM即Out ofMemory是标准linuxKernel的一种内存管理机制,LowMemory Killer在它基础上作了改进:
- OOM基于多个标准给每个进程打分,分最高的进程将被杀死;LowMemory Killer则用oom_adj和占用内存的大小来选择Bad进程;
- OOM在内存分配不足时调用,而LowMemory Killer每隔一段时间就会检查,一旦发现空闲内存低于某个阈值,则杀死Bad进程。
static intlowmem_adj[6] = {
0,
1,
6,
12,
};
static intlowmem_adj_size = 4;
static intlowmem_minfree[6] = {
3 * 512, /* 6MB,单位是4k */
2 * 1024, /* 8MB */
4 * 1024, /* 16MB */
16 * 1024, /* 64MB */
};
- lowmem_adj[6] : 中各项数值代表阈值的警戒级数;
- lowmem_minfree[6] :代表对应级数的剩余内存。
也就是说,当系统的可用内存小于6MB时,警戒级数为0;当系统可用内存小于8M而大于6M时,警戒级数为1;当可用内存小于64M大于16MB时,警戒级数为12。
Low memory killer的规则就是根据当前系统的可用内存多少来获取当前的警戒级数,
- 如果进程的oom_adj大于警戒级数并且最大,进程将会被杀死(具有相同oom_adj的进程,则杀死占用内存较多的)。
- oom_adj越小,代表进程越重要。一些前台的进程,oom_adj会比较小,而后台的服务,oom_adj会比较大,所以当内存不足的时候,Low memory killer必然先杀掉的是后台服务而不是前台的进程。
这里总结一下OOMlevels的更新:更新是在系统启动后,第一个configurationchange的消息到AMS的时候。adj值为一组固定的预定义的值;各个adj所对应的minfree阈值则根据系统的内存大小和屏幕的分辨率计算得出,各个设备还可以通过framework的configconfig_lowMemoryKillerMinFreeKbytesAdjust
和config_lowMemoryKillerMinFreeKbytesAbsolute来定制最终各个adj所对应的minfree阈值。
这里还要提一下Process是怎么set adj值的。
通过Process.setOomAdj(intpid, int amt)进行设置,其中在ActivityManagerService中updateOomAdjLocked()函数中就有调用。
2.LMK的工作机制
LMK开始工作时,
首先根据阈值表确定当前的警戒级数,则高于警戒级数的进程是待杀的范围。
然后遍历所有进程的oom_adj值,找到大于min_adj的进程,若找到多个,则把占用进程最大的进程存放在selected中。
最关键的一步就是,发送SIGKILL信息,杀掉该进程。
LMK低内存管理机制
LMK开始工作时, 首先根据阈值表确定当前的警戒级数,则高于警戒级数的进程是待杀的范围。然后遍历所有进程的oom_adj值,找到大于min_adj的进程,若找到多个,则把占用进程最大的进程存放在selected中。 最关键的一步就是,发送SIGKILL信息,杀掉该进程。
最后值得提到的就是ActivityManageService.java中的OOM了,主要是当后台进程数达到阀值ProcessList.MAX_HIDDEN_APPS 15后对被杀进程的选择,而且进程的oom_adj也会适当地被调整。
发现执行了lowmemorykiller的进程总共有3个:内核线程kswapd0、native app adbd和app com.example.outX。由此,我们推测lowmemorykiller大概有两种执行方式:
(1)是在kswapd0中,kswapd内核线程会被定期唤醒,当它发现系统内存不足的时候,它就会去执行lowmemorykiller杀进程,并回收内存。
(2)是在app中,当一个app请求分配内存,而系统又没有足够的内存可供分配时,则分配内存的请求将会先被阻塞,执行lowmemorykiller杀进程,回收内存之后再尝试着去分配内存。