zoukankan      html  css  js  c++  java
  • android的低内存管理器【转】

    本文转载自:http://blog.csdn.net/haitaoliang/article/details/22092321

    安卓应用不用太在意内存回收,首先Android上的应用是带有独立Java虚拟机的,也就是每开一个应用就会打开一个独立的虚拟机,虚拟机有自己的一套规则回收内存;同时和java的垃圾回收机制类似,android系统有一个规则来主动回收内存。进行内存回收有个阀值,只有低于这个值系统才会按一个列表来关闭用户不需要的东西。

    下面介绍android内核的低内存管理,由于上述的这个阈值存在,所以我们会看到android内存使用比例并不高。事实上高剩余内存并不影响速度,相反加快了下次启动应用的速度。这本来就是 android标榜的优势之一,如果人为去关闭进程,没有太大必要,用户体验也不好。比Linux的标准的OOM(Out Of Memory)机制更加灵活,它可以根据需要杀死进程以释放需要的内存 。源代码 位 于 drivers/staging/android/lowmemorykiller.c。

    linux使用OOM(Out Of Memory,内存不足)的机制来完成这个任务,该机制会在系统内存不足的情况下,选择一个进程并将其 Kill 掉。Android则使用了一个新的机制——Low Memory  Killer 来完成同样的任务。下面首先来看看 Low  MemoryKiller 机制的原理以及它是如何选择将被 Kill 的进程的。

    1.Low Memory Killer 的原理和机制

    Low Memory  Killer 在用户空间中指定了一组内存临界值,当其中的某个值与进程描述中的oom_adj值在同一范围时,该进程将被Kill掉。在“/sys/module/lowmemorykiller/parameters/adj”中指定oom_adj的最小值,在“/sys/module/lowmemorykiller/parameters/minfree” 中指定空闲页面的数量,所有的值都用一个逗号将其隔开且以升序排列。比如:把“0,8”写入到/sys/module/lowmemorykiller/parameters/adj中,把“1024,4096”写入到/sys/module/lowmemory-killer/parameters/minfree 中,就表示当一个进程的空闲存储空间下降到 4096 个页面时,oom_adj 值为 8 或者更大的进程会被 Kill 掉。同理,当一个进程发现空闲存储空间下降到 1024 个页面时, oom_adj 值为 0 或者更大的进程会被 Kill 掉。在 lowmemorykiller.c 中就指定了这样的默认值,如下所示:

    static int lowmem_adj[6] = {

    0,

    1,

    6,

    12,

    };

    static int lowmem_adj_size = 4;

    static size_t lowmem_minfree[6] = {

    3*512, // 单位页,6MB

    2*1024, // 8MB

    4*1024, // 16MB

    16*1024, // 64MB

    };

    static int lowmem_minfree_size = 4;

    这就说明,当一个进程的空闲空间下降到 3*512 个页面时,oom_adj 值为 0 或者更大的进 程会被 Kill 掉;当一个进程的空闲空间下降到 2*1024 个页面时,oom_adj 值为 10 或者更大的进程会被 Kill 掉,依此类推。

    进程的oom_adj值是由上层决定的,Android将进程分为6个等级,分别对应不同的lowmem_adj,它们按优先级顺序由高到低依次是:
         1前台进程(foreground)

     目前正在屏幕上显示的进程和一些系统进程。举例来说,Dialer,Storage,Google Search等系统进程就是前台进程;再举例来说,当你运行一个程序,如浏览器,当浏览器界面在前台显示时,浏览器属于前台进程(foreground),但一旦你按home回到主界面,浏览器就变成了后台程序(background)。我们最不希望终止的进程就是前台进程。

    2可见进程(visible)

     可见进程是一些不再前台,但用户依然可见的进程,举个例来说:widget、输入法等,都属于visible。这部分进程虽然不在前台,但与我们的使用也密切相关,我们也不希望它们被终止(还比如时钟、天气,新闻等widget用户肯定不希望被终止,那它们将无法同步,你也不希望输入法被终止,否则你每次输入时都需要重新启动输入法)。

    3桌面进程(home app)

     即launcher(桌面启动器),保证在多任务切换之后,可以快速返回到home界面而不需重新加载launcher。

    4次要服务(secondary server)

     目前正在运行的一些服务(主要服务,如拨号等,是不可能被进程管理终止的,故这里只谈次要服务),举例来说:谷歌企业套件,Gmail内部存储,联系人内部存储等。这部分服务虽然属于次要服务,但很一些系统功能依然息息相关,我们时常需要用到它们,所以也不太希望他们被终止。

    5后台进程(hidden)

     即是后台进程(background),就是我们通常意义上理解的启动后被切换到后台的进程,如浏览器,阅读器等。当程序显示在屏幕上时,他所运行的进程即为前台进程(foreground),一旦我们按home返回主界面(注意是按home,不是按back),程序就驻留在后台,成为后台进程(background)。

    后台进程的管理策略有多种:有较为积极的方式,一旦程序到达后台立即终止,这种方式会提高程序的运行速度,但无法加速程序的再次启动;也有较消极的方式,尽可能多的保留后台程序,虽然可能会影响到单个程序的运行速度,但在再次启动已启动的程序时,速度会有所提升。这里就需要用户根据自己的使用习惯找到一个平衡点。

    6内容供应节点(content provider)

    没有程序实体,仅提供内容供别的程序去用的,比如日历供应节点,邮件供应节点等。在终止进程时,这类程序应该优先杀死。

    init.rc(android上层代码)中对adj与minfree默认值进行设置,在内核里分别对应lowmem_adj和lowmem_minfree

    # Define the oom_adj values for the classes of processesthat can be

    # killed by the kernel.  These are used inActivityManagerService.

       setprop ro.FOREGROUND_APP_ADJ 0

       setprop ro.VISIBLE_APP_ADJ 1

       setprop ro.SECONDARY_SERVER_ADJ 2

       setprop ro.HIDDEN_APP_MIN_ADJ 7

       setprop ro.CONTENT_PROVIDER_ADJ 14

       setprop ro.EMPTY_APP_ADJ 15

    # Define the memory thresholds at which the above processclasses will

    # be killed.  These numbers are in pages (4k).

       setprop ro.FOREGROUND_APP_MEM 1536

       setprop ro.VISIBLE_APP_MEM 2048

       setprop ro.SECONDARY_SERVER_MEM 4096

       setprop ro.HIDDEN_APP_MEM 5120

       setprop ro.CONTENT_PROVIDER_MEM 5632

       setprop ro.EMPTY_APP_MEM 6144

    进程描述符中的 signal_struct->oom_adj 表示当内存短缺时进程被选择并 Kill 的优先级,取值范围是−17~15。如果是−17,则表示不会被选中,值越大越可能被选中。当某个进程被选中 后,内核会发送 SIGKILL 信号将其 Kill 掉,进程oom优先级别可以通过写/proc/<PID>/oom_adj进行设置。

    2.Low Memory Killer 的具体实现

    在了解了 Low Memory  Killer 的原理之后,我们再来看如何实现这个驱动。Low  MemoryKiller 驱动的实现位于drivers/misc/lowmemorykiller.c。

    该驱动的实现比较简单,在 初 始 化 函 数  lowmem_init   中 通 过  register_shrinker   注 册 了 一 个shrinker为lowmem_shrinker:

    static int __init lowmem_init(void)

    {

    register_shrinker(&lowmem_shrinker);

    return 0;

    }

    static void __exit lowmem_exit(void)

    {

    unregister_shrinker(&lowmem_shrinker);

    }

    module_init(lowmem_init);

    module_exit(lowmem_exit);

    退出时又调用了函数lowmem_exit,通过 unregister_shrinker来卸载被注册的lowmem_shrinker。其中lowmem_shrinker的定义如下:

    static struct shrinker lowmem_shrinker = {

    .shrink = lowmem_shrink,

    .seeks = DEFAULT_SEEKS * 16

    };

    lowmem_shrink 是这个驱动的核心实现,当内存不足时就会调用 lowmem_shrink 方法来 Kill掉某些进程shrink_slab -> do_shrinker_shrink->lowmem_shrink。kswapd线程将会遍历一张shrinker链表,下面来分析其具体实现,实现代码如下:

    对每个满足sig->oom_adj大于min_adj的进程,找到占用内存最大的进程存放在selected中:

    static int lowmem_shrink(struct shrinker *s, structshrink_control *sc)

    {

    struct task_struct*p;

    struct task_struct*selected = NULL;

    int rem = 0;

    int tasksize;

    int i;

    int min_adj =OOM_ADJUST_MAX + 1;

    intselected_tasksize = 0;

    int selected_oom_adj;

    int array_size =ARRAY_SIZE(lowmem_adj);

    int other_free =global_page_state(NR_FREE_PAGES);

    int other_file =global_page_state(NR_FILE_PAGES) -

                         global_page_state(NR_SHMEM);

    /*

     * If we already have a death outstanding, then

     * bail out right away; indicating to vmscan

     * that we have nothing further to offer on

     * this pass.

     *

     */

    if(lowmem_deathpending &&

        time_before_eq(jiffies,lowmem_deathpending_timeout))

         return 0;

    if (lowmem_adj_size< array_size)

         array_size =lowmem_adj_size;

    if(lowmem_minfree_size < array_size)

         array_size =lowmem_minfree_size;

    for (i = 0; i <array_size; i++) {

         if (other_free< lowmem_minfree[i] &&

             other_file < lowmem_minfree[i]) {

             min_adj = lowmem_adj[i];

             break;

         }

    }

    if(sc->nr_to_scan > 0)

         lowmem_print(3,"lowmem_shrink %lu, %x, ofree %d %d, ma %d ",

                  sc->nr_to_scan, sc->gfp_mask,other_free, other_file,

                  min_adj);

    rem =global_page_state(NR_ACTIVE_ANON) +

         global_page_state(NR_ACTIVE_FILE)+

        global_page_state(NR_INACTIVE_ANON) +

         global_page_state(NR_INACTIVE_FILE);

    if(sc->nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {

         lowmem_print(5,"lowmem_shrink %lu, %x, return %d ",

                  sc->nr_to_scan, sc->gfp_mask, rem);

         return rem;

    }

    selected_oom_adj =min_adj;

    read_lock(&tasklist_lock);

    for_each_process(p){

         structmm_struct *mm;

         structsignal_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 ",

                  p->pid, p->comm, oom_adj, tasksize);

    }

    if (selected) {

         lowmem_print(1,"send sigkill to %d (%s), adj %d, size %d ",

                  selected->pid, selected->comm,

                  selected_oom_adj, selected_tasksize);

         lowmem_deathpending= selected;

         lowmem_deathpending_timeout= jiffies + HZ;

         force_sig(SIGKILL,selected);

         rem -=selected_tasksize;

    }

    lowmem_print(4,"lowmem_shrink %lu, %x, return %d ",

              sc->nr_to_scan, sc->gfp_mask, rem);

    read_unlock(&tasklist_lock);

    return rem;

    }

    lowmem_shrink函数首先确定我们所定义的lowmem_adj和 lowmem_minfree 数组的大小(元素个数)是否一致,如果不一致则以最小的为基准。因为我们需要通过比较lowmem_minfree中的空闲储存空间的值,以确定最小min_adj值(当满足其条件时,通过其数组索引来寻找lowmem_adj中对应元素的值);然后使用循环对每一个进程块进行判断,通过min_adj来寻找满足条件的具体进程,并且找到满足条件的tasksize最大的进程;最后如果存在这样的进程,通过“force_sig(SIGKILL, selected)”发送一条SIGKILL信号到内核, Kill  掉被选中的 “selected”进程。

    最后简单描述一下标准 Linux 的 OOM Killer 机制。在分 配内存时,即mm/page_alloc.c中内存紧张会调用__alloc_pages_may_oom。oom_kill.c 最主要的一个函数是 out_of_memory,它选择一个bad进程Kill, Kill的方法同样是通过发送SIGKILL信号。在 out_of_memory 中通过调用select_bad_process 来 选择一个进程 Kill,选择的依据在oom_badness函数中实现,2.6.36后内核引入多个标准来给每个进程评分,评分最高的被选中并 Kill。一般而言,占用内存越多,/proc/pid/oom_score_adj越大,非root进程等因素越有可能被选中,可见比android要简单。

  • 相关阅读:
    PHPCMS V9后台表单向导用户提交的信息新增“修改”的功能
    phpcms V9最实用的23个调用代码
    phpcms V9推荐位无法调用自定义字段的解决方法
    将博客搬至CSDN
    爬取淘宝商品信息selenium+pyquery+mongodb
    selenium库
    requests库+ajax数据分析+多线程爬取头条图集
    requests微博爬取Ajax数据+mongoDB存储
    redis存储
    pymongo操作MongoDB
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/7464386.html
Copyright © 2011-2022 走看看