Android 内存管理 - Low Memory killer & OOM

本文深入探讨了Android系统中用于内存管理的Lowmemorykiller机制,详细阐述了其引入原因、工作原理及具体实现。从内存警戒值到进程评分机制,再到内存分页回收时的规则释放,全方位解析了如何在系统内存不足时,通过定时检查和评分机制,合理地选择并杀死进程,以释放内存,确保系统稳定运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android在内存管理上与linux有些小的区别。其中一个就是引入了Low memory killer .
1,引入原因
   Android是一个多任务系统,也就是说可以同时运行多个程序,这个大家应该很熟悉。一般来说,启动运行一个程序是有一定的时间开销的,因此为了加快运行速度,当你退出一个程序时,Android并不会立即杀掉它,这样下次再运行该程序时,可以很快的启动。随着系统中保留的程序越来越多,内存肯定会出现不足,low memory killer就是在系统内存低于某值时,清除相关的程序,保障系统保持拥有一定数量的空闲内存。
   Android中,进程的生命周期都是由系统控制的,即使用户关掉了程序,进程依然是存在于内存之中。这样设计的目的是为了下次能快速启动。当然,随着系统运行时间的增长,内存会越来越少。Android Kernel 会定时执行一次检查,杀死一些进程,释放掉内存。
    那么,如何来判断,那些进程是需要杀死的呢?答案就是我们的标题:Low memory killer机制。
    Android 的Low memory killer是基于linux的OOM(out of memory)  规则改进而来的。 OOM通过一些比较复杂的评分机制,对进程进行打分,然后将分数高的进程判定为bad进程,杀死并释放内存。OOM只有当系统内存不足的时候才会启动检查,而Low memory killer 则是定时进行检查。
     Low memory killer 主要是通过进程的oom_adj 来判定进程的重要程度。oom_adj的大小和进程的类型以及进程被调度的次序有关。
     Low memory killer 的具体实现可参看:kernel/drivers/misc/lowmemorykiller.c 
     其原理很简单,在linux中,存在一个kswapd的内核线程,当linux回收存放分页的时候,kswapd线程将会遍历一张shrinker链表,并执行回调,定义如下:
struct shrinker{
int (*shrink)(int nr_to_scan, gfp_t gfp_mask);
int seeks;
struct list_head list;
long nr;
};
#define DEFAULT_SEEKS 2
extern void register_shrinker(struct shrinker *);
extern void unregiter_shrinker(struct shrinker *);

所以只要注册 Shrinker,变可以在内存分页回收时根据规则释放内存,下面我们来看看其实现。 
首先定义shrinker结构体,lowmem_shrink为回调函数的指针,当有内存分页回收的时候,这个函数将会被调用。
static struct shrinker lowmem_shrinker = {
   .shrink = lowmem_shrink,
   .seeks = DEFAULT_SEEKS * 16
};

2. 基本原理和重要概念
   Low memory killer根据两个原则,进程的重要性和释放这个进程可获取的空闲内存数量,来决定释放的进程。
(1)进程的重要性,由task_struct->signal_struct->oom_adj决定。
Android将程序分成以下几类,按照重要性依次降低的顺序:
名称 oom_adj 解释 
FOREGROUD_APP 0 前台程序,可以理解为你正在使用的程序 
VISIBLE_APP 1 用户可见的程序 
SECONDARY_SERVER 2 后台服务,比如说QQ会在后台运行服务 
HOME_APP 4 HOME,就是主界面 
HIDDEN_APP 7 被隐藏的程序 
CONTENT_PROVIDER 14 内容提供者, 
EMPTY_APP 15  空程序,既不提供服务,也不提供内容
其中每个程序都会有一个oom_adj值,这个值越小,程序越重要,被杀的可能性越低。
(2)进程的内存,通过get_mm_rss获取,在相同的oom_adj下,内存大的,优先被杀。
(3)那内存低到什么情况下,low memory killer开始干活呢?Android提供了两个数组,一个lowmem_adj,一个lowmem_minfree。前者存放着oom_adj的阀值,后者存放着minfree的警戒值,以page为单位(4K)。
oom_adj 内存警戒值( 以4K为单位)
0 1536 
1 2048 
2 4096 
7 5120 
14 5632 
15 6144
3.源码解析
module_init(lowmem_init);
 module_exit(lowmem_exit);
    模块加载和退出的函数,主要的功能就是register_shrinker和unregister_shrinker结构体lowmem_shrinker。主要是将函数lowmem_shrink注册到shrinker链表里,在mm_scan调用。
下面详细的介绍这个函数:
 for (i = 0; i < array_size; i++) {
        if (other_file < lowmem_minfree[i]) {
            min_adj = lowmem_adj[i];
            break;
        }
    }
other_file,系统的空闲内存数,根据上面的逻辑判断出,low memory killer需要对adj高于多少(min_adj)的进程进行分析是否释放。
  if (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
        lowmem_print(5, "lowmem_shrink %d, %x, return %d\n",
                 nr_to_scan, gfp_mask, rem);
        return rem;
    }
  判断,系统当前的状态是否需要进行low memory killer。
for_each_process(p) {
        struct mm_struct *mm;
        struct signal_struct *sig;
        int oom_adj;
        task_lock(p);
        mm = p->mm;
        sig = p->signal;
        if (!mm || !sig) {
            task_unlock(p);
            continue;
        }
        oom_adj = sig->oom_adj;
        if (oom_adj < min_adj) {
            task_unlock(p);
            continue;
        }
        tasksize = get_mm_rss(mm);
        task_unlock(p);
        if (tasksize <= 0)
            continue;
        if (selected) {
            if (oom_adj < selected_oom_adj)
                continue;
            if (oom_adj == selected_oom_adj &&
                tasksize <= selected_tasksize)
                continue;
        }
        selected = p;
        selected_tasksize = tasksize;
        selected_oom_adj = oom_adj;
        lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
                 p->pid, p->comm, oom_adj, tasksize);
    }
对每个sig->oom_adj大于min_adj的进程,找到占用内存最大的进程存放在selected中。
if (selected) {
        if (fatal_signal_pending(selected)) {
            pr_warning("process %d is suffering a slow death\n",
                   selected->pid);
            read_unlock(&tasklist_lock);
            return rem;
        }
        lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
                 selected->pid, selected->comm,
                 selected_oom_adj, selected_tasksize);
        force_sig(SIGKILL, selected);
        rem -= selected_tasksize;
    }
 发送SIGKILL信息,杀掉该进程。
关于Low Memory Killer的分析就到这里,在了解了其机制和原理之后,我们发现它的实现非常简单,与标准的Linux OOM机制类似,只是实现方式稍有不同。标准Linux的OOM Killer机制在mm/oom_kill.c中实现,且会被__alloc_pages_may_oom调用(在分配内存时,即mm/page_alloc.c中)。oom_kill.c最主要的一个函数是out_of_memory,它选择一个bad进程Kill,Kill的方法同样是通过发送SIGKILL信号。在out_of_memory中通过调用select_bad_process来选择一个进程Kill,选择的依据在badness函数中实现,基于多个标准来给每个进程评分,评分最高的被选中并Kill。一般而言,占用内存越多,oom_adj就越大,也就越有可能被选中。

参考地址:

http://my.oschina.net/wolfcs/blog/288259

http://www.cnblogs.com/angeldevil/archive/2013/05/21/3090872.html

http://blog.youkuaiyun.com/arnoldlu/article/details/8054184

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值