Android 内存管理 - Low Memory killer & OOM

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.csdn.net/arnoldlu/article/details/8054184

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/sys/module/lowmemorykiller/parameters/ 是一个内核参数路径,用于控制 Android 系统中的低内存杀死(Low Memory Killer)机制。以下是该路径下常见参数的含义: 1. minfree:表示当系统内存不足时,内核会杀死一些进程以释放内存。minfree 参数指定了系统可用内存的阈值,当可用内存低于该阈值时,内核会开始杀死进程。minfree 参数由六个数字组成,分别表示系统内存的不同阶段。例如“4096, 8192, 12288, 16384, 20480, 24576”表示当可用内存低于 4MB、8MB、12MB、16MB、20MB、24MB 时,内核会开始杀死进程。 2. lmk_minfree_ratio:与 minfree 类似,但是使用的是内存占比而非固定大小。例如“0, 1, 2, 4, 6, 8”表示当可用内存低于总内存的 0%、1%、2%、4%、6%、8% 时,内核会开始杀死进程。 3. lmk_minfree_adj:表示在低内存情况下,哪些进程会被优先杀死。进程的优先级由 Android 中的 OOM(Out of Memory)机制决定。lmk_minfree_adj 参数可以设置不同 OOM 分级的进程被杀死的 minfree 阈值。例如“0, 1, 2, 4, 9, 15”表示 OOM_ADJ_MIN_FREE(值为0)的进程在可用内存低于第一个 minfree 阈值时被杀死,而 OOM_ADJ_FOREGROUND(值为2)的进程在可用内存低于第三个 minfree 阈值时被杀死。 4. lmk_lru_percent:表示当内存紧张时,内核会清理掉多少 LRU(Least Recently Used)进程。LRU 进程指的是最近最少使用的进程,也就是说它们在内存中待的时间最久,但占用的内存却很少。lmk_lru_percent 参数指定了当可用内存低于 lmk_lru_percent% 时,内核会清理掉 LRU 进程。 5. lmk_inactive_file_ratio:表示当内存紧张时,内核会清理掉多少页缓存。页缓存指的是文件系统中的数据,例如应用程序的缓存文件、系统的日志文件等。lmk_inactive_file_ratio 参数指定了当可用内存低于总内存的 lmk_inactive_file_ratio% 时,内核会清理掉页缓存。 6. lmk_active_file_ratio:表示当内存紧张时,内核会清理掉多少页缓存。与 lmk_inactive_file_ratio 类似,但是 lmk_active_file_ratio 参数指定的是正在活跃使用的页缓存。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值