zoukankan      html  css  js  c++  java
  • Android Low Memory Killer

    Low Memory Killer的原理

      在Android中,即使当用户退出应用程序之后,应用程序的进程也还是存在于系统中,这样是为了方便程序的再次启动,但是这样的话,随着打开的程序数量的增加,系统的内存会变得不足,就需要杀掉一部分进程以释放内存空间。至于是否需要杀死一些进程和哪些进程需要被杀死,是通过Low Memory Killer机制来进行判定的。

      Android的Low Memory Killer基于Linux的OOM机制,在Linux中,内存是以页面为单位分配的,当申请页面分配时如果内存不足会通过以下流程选择bad进程来杀掉从而释放内存:

    alloc_pages -> out_of_memory() -> select_bad_process() -> badness()

      在Low Memory Killer中通过进程的oom_adj与占用内存的大小决定要杀死的进程,oom_adj越小越不容易被杀死。

      Low Memory Killer Driver在用户空间指定了一组内存临界值及与之一一对应的一组oom_adj值,当系统剩余内存位于内存临界值中的一个范围内时,如果一个进程的oom_adj值大于或等于这个临界值对应的oom_adj值就会被杀掉。

      可以通过修改/sys/module/lowmemorykiller/parameters/minfree与/sys/module/lowmemorykiller/parameters/adj来改变内存临界值及与之对应的oom_adj值。minfree中数值的单位是内存中的页面数量,一般情况下一个页面是4KB。
      比如如果向/sys/module/lowmemorykiller/parameters/adj写入0,8,向/sys/module/lowmemorykiller/parameters/minfree中写入1024,4096,假设一个页面大小为4KB,这样当系统空闲内存位于1024*4~4096*4KB之间时oom_adj大于等于8的进程就会被杀掉。

      在lowmemorykiller.c中定义了阈值表的默认值,可以通过init.rc自定义:

    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; 

      在init.rc中定义了init进程的oom_adj为-16,不可能会被杀死(init的PID是1):

    on early-init
        # Set init and its forked children's oom_adj.
        write /proc/1/oom_adj -16

      在Linux中有一个kswapd的内核线程,当linux回收内存分页的时候,kswapd线程将会遍历一张shrinker链表,并执行回调,定义如下:

    
    
    /*
     * A callback you can register to apply pressure to ageable caches.
     *
     * 'shrink' is passed a count 'nr_to_scan' and a 'gfpmask'.  It should
     * look through the least-recently-used 'nr_to_scan' entries and
     * attempt to free them up.  It should return the number of objects
     * which remain in the cache.  If it returns -1, it means it cannot do
     * any scanning at this time (eg. there is a risk of deadlock).
     *
     * The 'gfpmask' refers to the allocation we are currently trying to
     * fulfil.
     *
     * Note that 'shrink' will be passed nr_to_scan == 0 when the VM is
     * querying the cache size, so a fastpath for that case is appropriate.
    */
    struct shrinker {
        int (*shrink)(int nr_to_scan, gfp_t gfp_mask);
        int seeks;      /* seeks to recreate an obj */
    
        /* These are for internal use */
        struct list_head list;
        long nr;        /* objs pending delete */
    };
    #define DEFAULT_SEEKS 2 /* A good number if you don't know better. */
    extern void register_shrinker(struct shrinker *);
    extern void unregister_shrinker(struct shrinker *);

      通过register_shrinker与unregister_shrinker向shrinker链表中添加或移除回调。当注册Shrinker后就可以在回收内存分页时按自己定义的规则释放内存。

      Android Low Memory Killer的代码在drivers/staging/android/lowmemorykiller.c中,通过以下代码在模块初始化时注册Shrinker:

    static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask);
     
    static struct shrinker lowmem_shrinker = {
            .shrink = lowmem_shrink,
            .seeks = DEFAULT_SEEKS * 16
    };
    
    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_shrink函数。

    Low Memory Killer的实现

      lowmem_shrink的定义如下:

    static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask)
    {
            struct task_struct *p;
            struct task_struct *selected = NULL;
            int rem = 0;
            int tasksize;
            int i;
            int min_adj = OOM_ADJUST_MAX + 1;
            int selected_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);
    
            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 (nr_to_scan > 0)
                    lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n",
                                 nr_to_scan, 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 (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;
            }
            selected_oom_adj = min_adj;
    
            read_lock(&tasklist_lock);
            for_each_process(p) {
                    struct mm_struct *mm;
                    int oom_adj;
    
                    task_lock(p);
                    mm = p->mm;
                    if (!mm) {
                            task_unlock(p);
                            continue;
                    }
                    oom_adj = mm->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);
            }
            if (selected) {
                    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;
            }
            lowmem_print(4, "lowmem_shrink %d, %x, return %d\n",
                         nr_to_scan, gfp_mask, rem);
            read_unlock(&tasklist_lock);
            return rem;
    }
    View Code

      分开来看这段代码,首先取得内存阈值表的大小,取阈值表数组大小与lowmem_adj_size,lowmem_minfree_size的较小值,然后通过globa_page_state获得当前剩余内存的大小,然后跟内存阈值表中的阈值相比较获得min_adj与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);
    
    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;
        }
    }
    selected_oom_adj = min_adj;

      遍历所有进程找到oom_adj>min_adj并且占用内存大的进程:

    read_lock(&tasklist_lock);
    for_each_process(p) {
        struct mm_struct *mm;
        int oom_adj;
    
        task_lock(p);
        mm = p->mm;
        if (!mm) {
            task_unlock(p);
            continue;
        }
        oom_adj = mm->oom_adj;
        //获取task_struct->struct_mm->oom_adj,如果小于警戒值min_adj不做处理
        if (oom_adj < min_adj) {
            task_unlock(p);
            continue;
        }
        //如果走到这里说明oom_adj>=min_adj,即超过警戒值
        //获取内存占用大小,若<=0,不做处理
        tasksize = get_mm_rss(mm);
        task_unlock(p);
        if (tasksize <= 0)
            continue;
        //如果之前已经先择了一个进程,比较当前进程与之前选择的进程的oom_adj与内存占用大小,如果oom_adj比之前选择的小或相等而内存占用比之前选择的进程小,不做处理。
        if (selected) {
            if (oom_adj < selected_oom_adj)
                continue;
            if (oom_adj == selected_oom_adj &&
                tasksize <= selected_tasksize)
                continue;
        }
        //走到这里表示当前进程比之前选择的进程oom_adj大或相等但占用内存大,选择当前进程
        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);
    }

      如果选择出了符合条件的进程,发送SIGNAL信号Kill掉:

    if (selected) {
        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;
    }

    oom_adj与上层Process Importance的关系

      我们知道,在上层进程按重要性可以分为:Foreground process,Visible process,Service process,Background process与Empty process,那么这些重要性怎么与Low Memory Killer中的oom_adj对应起来的呢?

      在ActivityManager.RunningAppProcessInfo中我们可以看到如下关于importance的定义:

    /**
     * Constant for {@link #importance}: this is a persistent process.
     * Only used when reporting to process observers.
     * @hide
     */
    public static final int IMPORTANCE_PERSISTENT = 50;
    
    /**
     * Constant for {@link #importance}: this process is running the
     * foreground UI.
     */
    public static final int IMPORTANCE_FOREGROUND = 100;
    
    /**
     * Constant for {@link #importance}: this process is running something
     * that is actively visible to the user, though not in the immediate
     * foreground.
     */
    public static final int IMPORTANCE_VISIBLE = 200;
    
    /**
     * Constant for {@link #importance}: this process is running something
     * that is considered to be actively perceptible to the user.  An
     * example would be an application performing background music playback.
     */
    public static final int IMPORTANCE_PERCEPTIBLE = 130;
    
    /**
     * Constant for {@link #importance}: this process is running an
     * application that can not save its state, and thus can't be killed
     * while in the background.
     * @hide
     */
    public static final int IMPORTANCE_CANT_SAVE_STATE = 170;
    
    /**
     * Constant for {@link #importance}: this process is contains services
     * that should remain running.
     */
    public static final int IMPORTANCE_SERVICE = 300;
    
    /**
     * Constant for {@link #importance}: this process process contains
     * background code that is expendable.
     */
    public static final int IMPORTANCE_BACKGROUND = 400;
    
    /**
     * Constant for {@link #importance}: this process is empty of any
     * actively running code.
     */
    public static final int IMPORTANCE_EMPTY = 500;

      这些常量表示了Process的Importance等级,而在ProcessList中我们会发现关于adj的一些定义:

    // This is a process only hosting activities that are not visible,
    // so it can be killed without any disruption.
    static final int HIDDEN_APP_MAX_ADJ = 15;
    static int HIDDEN_APP_MIN_ADJ = 9;
    
    // The B list of SERVICE_ADJ -- these are the old and decrepit
    // services that aren't as shiny and interesting as the ones in the A list.
    static final int SERVICE_B_ADJ = 8;
    
    // This is the process of the previous application that the user was in.
    // This process is kept above other things, because it is very common to
    // switch back to the previous app.  This is important both for recent
    // task switch (toggling between the two top recent apps) as well as normal
    // UI flow such as clicking on a URI in the e-mail app to view in the browser,
    // and then pressing back to return to e-mail.
    static final int PREVIOUS_APP_ADJ = 7;
    
    // This is a process holding the home application -- we want to try
    // avoiding killing it, even if it would normally be in the background,
    
    // because the user interacts with it so much.
    static final int HOME_APP_ADJ = 6;
    
    // This is a process holding an application service -- killing it will not
    // have much of an impact as far as the user is concerned.
    static final int SERVICE_ADJ = 5;
    
    // This is a process currently hosting a backup operation.  Killing it
    // is not entirely fatal but is generally a bad idea.
    static final int BACKUP_APP_ADJ = 4;
    
    // This is a process with a heavy-weight application.  It is in the
    // background, but we want to try to avoid killing it.  Value set in
    // system/rootdir/init.rc on startup.
    static final int HEAVY_WEIGHT_APP_ADJ = 3;
    
    // This is a process only hosting components that are perceptible to the
    // user, and we really want to avoid killing them, but they are not
    // immediately visible. An example is background music playback.
    static final int PERCEPTIBLE_APP_ADJ = 2;
    
    // This is a process only hosting activities that are visible to the
    // user, so we'd prefer they don't disappear.
    static final int VISIBLE_APP_ADJ = 1;
    
    // This is the process running the current foreground app.  We'd really
    // rather not kill it!
    static final int FOREGROUND_APP_ADJ = 0;
    
    // This is a system persistent process, such as telephony.  Definitely
    // don't want to kill it, but doing so is not completely fatal.
    static final int PERSISTENT_PROC_ADJ = -12;
    
    // The system process runs at the default adjustment.
    static final int SYSTEM_ADJ = -16;

      我们可以看到:

    static final int PREVIOUS_APP_ADJ = 7;
    static final int HOME_APP_ADJ = 6;

      并不是所有的Background process的等级都是相同的。

      关于ADJ与Importance的值都找到了,那么它们是怎么对应起来的呢?Activity实际是由ActivityManagerService来管理的,在ActivityManagerService中我们可以找到以下函数:

    static int oomAdjToImportance(int adj, ActivityManager.RunningAppProcessInfo currApp) {
        if (adj >= ProcessList.HIDDEN_APP_MIN_ADJ) {
            if (currApp != null) {
                currApp.lru = adj - ProcessList.HIDDEN_APP_MIN_ADJ + 1;
            }
            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
        } else if (adj >= ProcessList.SERVICE_B_ADJ) {
            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
        } else if (adj >= ProcessList.HOME_APP_ADJ) {
            if (currApp != null) {
                currApp.lru = 0;
            }
            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
        } else if (adj >= ProcessList.SERVICE_ADJ) {
            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
        } else if (adj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE;
        } else if (adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE;
        } else if (adj >= ProcessList.VISIBLE_APP_ADJ) {
            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
        } else {
            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
        }
    }

      在这个函数中实现了根据adj设置importance的功能。

      我们还可以看到SERVICE还分为SERVICE_B_ADJ与SERVICE_ADJ,等级是不一样的,并不是所有Service的优先级都比Background process的优先级高。当调用Service的startForeground后,Service的importance就变为了IMPORTANCE_PERCEPTIBLE(在记忆中曾经将Service设置为foreground并打印出其importance的值与IMPORTANCE_PERCEPTIBLE相等),对应的adj是PERCEPTIBLE_APP_ADJ,即2,已经很难被系统杀死了。

    // This is a system persistent process, such as telephony.  Definitely
    // don't want to kill it, but doing so is not completely fatal.
    static final int PERSISTENT_PROC_ADJ = -12;
    
    // The system process runs at the default adjustment.
    static final int SYSTEM_ADJ = -16;

      像电话等进程的adj为-12已基本不可能被杀死了,而在前面已经看到了,init.rc中将init进程的oom_adj设置为了-16,已经是永生进程了。

    相关链接:

    lowermemorykiller.txt

    lowermemorykiller.c

    shrinker_list

  • 相关阅读:
    123457123457#0#-----com.tym.YuErBaiKeTYM--前拼后广--育儿百科
    123457123456#0#-----com.tym.XueYingYu01--前拼后广--小学英语tym
    123457123456#0#-----com.cym.shuXue02--前拼后广--开心学数学
    Spring事务失效的2种情况
    算法之排序
    JDK、Spring和Mybatis中使用到的设计模式
    MyBatis中#{}和${}的区别详解
    Redis为什么这么快
    java多线程之ScheduleThreadPoolExecutor
    java多线程之ThreadPoolExecutor
  • 原文地址:https://www.cnblogs.com/angeldevil/p/3090872.html
Copyright © 2011-2022 走看看