Android Low Memory Killer

The principle of Low Memory Killer

  In Android, even when the user quits the application, the application process still exist in the system, it is in order to facilitate the process starts again, however, with the increase in the number of open applications, system memory will become insufficient, it is necessary to kill a part of the process to release the memory space. As to whether need to kill some process and what processes need to be killed, is through the Low Memory Killer mechanism to make decisions.

  Low Memory Killer Android OOM mechanism based on Linux, in Linux, the memory is in page unit distribution, when the application page allocation if the memory will through the following process bad process to kill in order to release the memory:

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

  Through the oom_adj and memory process decides to kill the process at the Low Memory Killer, oom_adj smaller and not easy to be killed.

  Low Memory Killer Driver in user space specifies a set of memory critical value of one one corresponds to a set of oom_adj values, when a range of system the remaining memory in memory in the critical value, if a process oom_adj value is greater than or equal to the critical value of the corresponding oom_adj value will be killed.

  You can modify the /sys/module/lowmemorykiller/parameters/minfree and /sys/module/lowmemorykiller/parameters/adj to change the memory threshold and the corresponding oom_adj value. Minfree numerical unit is the number of pages in memory, in general a page is 4KB.

  For example, if you write to /sys/module/lowmemorykiller/parameters/adj 0,8, to write to the /sys/module/lowmemorykiller/parameters/minfree 10244096, a page size is assumed to be 4KB, so when the system is idle memory between 1024*4~4096*4KB when oom_adj is greater than or equal to 8 of the process will be killed.

  Lowmemorykiller.c is defined in the default threshold table, through the init.rc custom:

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 is defined in the init process of oom_adj to -16, may not be killed (init PID 1):

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

  There is a kswapd kernel threads in Linux, when Linux recovery memory paging, kswapd thread will traverse a shrinker list, and execute the callback, defined as follows:

/*
 * 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 *);

  Add or remove a callback to the shrinker list by register_shrinker and unregister_shrinker. When the free memory after the Shrinker is registered to its own definition in the recovery of memory paging rules.

  Android Low Memory Killer code in drivers/staging/android/lowmemorykiller.c, Shrinker registered in the module initialization by the following code:

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);

  So you can call the lowmem_shrink function in the recovery of memory paging.

Implementation of Low Memory Killer

  Lowmem_shrink is defined as follows:

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

  From this code, first obtain the memory threshold table size, threshold table array size and lowmem_adj_size, lowmem_minfree_size value of the smaller, and then get the remaining memory size by globa_page_state, and then follow the threshold memory threshold table compared to min_adj and 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;

  Through all of the processes to find oom_adj> min_adj and occupies large memory process:

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;
    //Gets a task_struct-> struct_mm-> oom_adj, if less than the warning value min_adj is not processed
    if (oom_adj < min_adj) {
        task_unlock(p);
        continue;
    }
    //If you come to here that oom_adj> =min_adj, which is more than the warning value;
    //Access to the memory size, if <=0, do not deal with
    tasksize = get_mm_rss(mm);
    task_unlock(p);
    if (tasksize <= 0)
        continue;
    //If you had to choose a process, comparing the current process and before the selection process of oom_adj and memory size, if the oom_adj than before the selection of small or equal and memory footprint than before the selection process is small, do not deal with. 
    if (selected) {
        if (oom_adj < selected_oom_adj)
            continue;
        if (oom_adj == selected_oom_adj &&
            tasksize <= selected_tasksize)
            continue;
    }
    //Here said the current process than before the selection process in oom_adj or equivalent but needs large memory, select the current process
    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 you select a meet the conditions of the process, send the SIGNAL 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;
}

The relationship between oom_adj and upper Process Importance

  We know that, according to the importance of the upper process can be divided into: Foreground process, Visible process, Service process, Background process and Empty process, then the importance and Low Memory in Killer oom_adj correspondence.?

  In ActivityManager.RunningAppProcessInfo we can see the following definition of 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;

  These constant indicates the Process Importance level, and in ProcessList we will find some on the definition of 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;

  We can see:

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

  Not all Background process levels are the same.

  On the ADJ and Importance values are found, so how can they correspond to each other? Activity is managed by ActivityManagerService, we can find the following functions in 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;
    }
}

  This function is implemented in a set based on adj importance function.

  We can also see the SERVICE is divided into SERVICE_B_ADJ and SERVICE_ADJ, grade is not the same, not all of the priority of Service than Background process high priority. When you call the Service startForeground, Service importance changed to IMPORTANCE_PERCEPTIBLE (in the memory used to set the Service to foreground and print out the equal its importance value and IMPORTANCE_PERCEPTIBLE), corresponding to the adj PERCEPTIBLE_APP_ADJ, 2, has been difficult to be killed.

// 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;

  Like the telephone, the process adj -12 has basically could not be killed, and saw in the previous section, init.rc, init process of oom_adj set to -16, is the life process.


Related links:

lowermemorykiller.txt

lowermemorykiller.c

shrinker_list

Posted by Tess at November 14, 2013 - 5:37 AM