Low memory killer是安卓内存管理的一种策略,其主要目的在于当系统内存不足时按照进程的重要性程度杀掉一些进程来保证系统有足够内存。
Low memory killer
Low memory killer机制实现是在kernel里面,具体实现可参看:kernel/drivers/staging/android/lowmemorykiller.c,。其机制也比较简单,主要分为两个两面:
1)什么时候运行;
2)运行时根据什么来决定杀死哪个进程。
先看第一点,当你看到lowmemorykiller源码,会看到 initcall的如下操作:
static int __init lowmem_init(void)
{
register_shrinker(&lowmem_shrinker);
return 0;
}
相关lowmem_shrinker数据结构如下:
static struct shrinker lowmem_shrinker = {
.scan_objects = lowmem_scan,
.count_objects = lowmem_count,
.seeks = DEFAULT_SEEKS * 16
};
从代码可以看到,真正执行扫描并杀进程的是lowmem_scan这个函数。那么是谁来调用的呢?当当当。。。这个大功臣就是kswapd。kswapd是一个内核线程,它会在回收内存分页时遍历shrinker链表并执行回调,对于lowmemorykiller而言,也就是注册进去的lowmen_scan。
第一个问题解决了,那么我们看下第二个问题,如何决定杀死哪个进程?我们可以看到在lowmemorykiller.c中存在两个阈值表:
static int lowmem_adj[6] = {
0,
1,
6,
12,
};
static int lowmem_minfree[6] = {
3 * 512, /*6MB */
2 * 1024, /*8MB */
4 * 1024, /*16MB */
16 * 1024, /* 64MB */
};
对于这两张表,lowmem_adj代表警戒的级数,lowmem_minfree代表对应级数的内存。对于安卓而言,实际并不会使用这个默认表。而是会通过module_param_named将这两张表开放给文件系统的/sys/module/lowmemorykiller/parameters目录,adj对应于lowmem_adj,minfree对应与lowmem_minfree。而AMS会根据系统的屏幕分辨率以及物理内存去更新这两个表对应的内容,详细参考ProcessList的applyDisplaySize()方法以及后续调用的updateOomLevels()。
从系统应用角度而言,主要层级对应如下:
系统会根据minfree中对应的值去杀掉对应等级的进程。如果不想App被杀掉,系统应用可以考虑添加android:persistent属性,或者在lowmemorykiller里面添加白名单处理。
native lmkd
安卓L之后,提供了一种用户空间native层面的lmkd方案。该机制不同于内核lowmemorykiller的触发机制,而是通过vmpressure来触发。
首先看所需的配置:
内核方面需要使能CONFIG_MEMCG=y以及CONFIG_MEMCG_SWAP=y来使能vmpressure功能,如果想使能memcg更多功能,还可以使能CONFIG_MEMCG_SWAP_ENABLED=y和CONFIG_MEMCG_KMEM=y来限制swap空间以及内存空间等资源的大小。
用户空间方面,如果要使能lmkd还需要配置ro.config.low_ram=true。另外还有其他一些配置,可参考lmkd.c main中的配置项。
这两点满足了,那么由于在init.rc中已经配置了lmkd进程的启动,在lmkd初始化过程中,就会根据是否是能了memcfg以及ro.config.low_ram决定是否使能给予vmpressure的lmkd。
lmkd会注册两种类型的vmpressure事件监听。一种是medium类型的内存压测,另一种是critical类型的内存压测,这两种压力的门限在内核中分别是60%和95%。计算公式如下:pressure = (1-reclaimed/scanned)100%, 也就是说pressure等于不可回收的内存所占的总内存的比例。一旦到达了这个门限,那么用户空间的epoll调用就会监听到相应的事件从而选择杀进程操作。
与之相关的还有几个配置,包括:
ro.lmk.medium : 中等内存压力时可杀的app对应的最小adj, 默认800
ro.lmk.critical: critical压力下可杀的app对应的最小adj,最小是0
ro.lmk.upgrade_pressure: medium内存压力时,需要升级为critcal对应的内存swappiness比例,默认50
ro.lmk.downgrade_pressure: critical内存压力时,需要降级为medium对应的内存swappiness比例,默认60.
另外,lmkd还有一个localsocket和AMS通信,用于以下几个方面用途:
1)更新lowmemorykiller中adj和内存的门限。
2)更新pid的list中对应的adj。
3)移除已经被杀掉的app进程。
总结
总结这两种实现方式,内核空间的lowmemorykiller控制更为精细,但是在交互和控制上不如lmkd。lmkd的优势在于控制上更为方便,至于说回收速度等方面的优势,不好评判,也许是lmkd实现者的自我说法。有关lmkd的演进可参考:https://blog.csdn.net/alien75/article/details/53322769?utm_source=blogxgwz0。