LMK 内存管理

1 low memory killer

Android的lowmemory killer是基于linux的OOM(out ofmemory)规则改进而来的。OOM通过一些比较复杂的评分机制,对运行进程进行打分,然后将分数高的进程判定为bad进程,杀死进程并释放内存。OOM只有当系统内存不足的时候才会启动检查,而lowmemory killer则不仅是在应用程序分配内存发现内存不足时启动检查,它也会定时地进行检查。

Low memory killer主要是通过进程的oom_adj来判定进程的重要程度的。oom_adj的大小和进程的类型以及进程被调度的次序有关。

Low memory killer的具体实现在kernel,比如对于androidL,代码在:/local/sourcecode/AndroidLSoul45/kernel-3.10/drivers/staging/android/lowmemorykiller.c。由于是根据MTK释放的androidL源码来研究,里面难免有相当篇幅是MTK自己添加的,目的是定制mtk自己memroy优化方案。本文介绍的时候可能会忽略这一部分代码,回归android原生系统。

1.1 基本原理

Low Memory Killer与OOM的区别:OOM即Out ofMemory是标准linuxKernel的一种内存管理机制,LowMemory Killer在它基础上作了改进:OOM基于多个标准给每个进程打分,分最高的进程将被杀死;LowMemory Killer则用oom_adj和占用内存的大小来选择Bad进程,OOM在内存分配不足时调用,而LowMemory Killer每隔一段时间就会检查,一旦发现空闲内存低于某个阈值,则杀死Bad进程。

基本实现原理:在linux中,存在一个名为kswapd的内核线程,当linux回收存放分页的时候,kswapd线程将会遍历一张shrinker链表,并执行回调,或者某个app分配内存,发现可用内存不足时,则内核会阻塞请求分配内存的进程分配内存的过程,并在该进程中去执行lowmemorykiller来释放内存。(具体kswapd怎么工作,没有研究)。

结构体shrinker的定义在AndroidLSoul45/kernel-3.10/include/linux/shrinker.h,具体如下:

struct shrinker {

       int (*shrink)(struct shrinker *, structshrink_control *sc);

       int seeks;      /* seeks to recreate an obj */

       long batch;   /* reclaim batch size, 0 = default */

 

       /* These are for internal use */

       struct list_head list;

       atomic_long_t nr_in_batch; /* objspending delete */

};

#defineDEFAULT_SEEKS 2 /* A good number if you don't know better. */

extern voidregister_shrinker(struct shrinker *);

extern void unregister_shrinker(structshrinker *);

#endif

根据上面的代码逻辑可以看出,只要注册shrinker,就可以在内存分页回收时根据规则释放内存。下面具体看看lowmemorykiller.c里是怎么实现的。

1.2 lowmemorykiller实现机制

下面具体介绍lowmemorykiller的原理。

1.2.1初始化与注销

//module_init(lowmem_init);android4.4采用这种方式

late_initcall(lowmem_init);

module_exit(lowmem_exit);

       模块加载和退出的函数,主要的功能就是register_shrinker和unregister_shrinker结构体lowmem_shrinker。主要是将函数lowmem_shrink注册到shrinker链表里。初始化模块时进行注册,模块退出时注销。

static structshrinker lowmem_shrinker = {

       .shrink = lowmem_shrink,//函数指针

       .seeks = DEFAULT_SEEKS * 16

};

//注册

static int __initlowmem_init(void)

{

#ifdef CONFIG_ZRAM

       vm_swappiness = 100;

#endif

       task_free_register(&task_nb);

       register_shrinker(&lowmem_shrinker);

       return 0;

}

//注销

static void __exitlowmem_exit(void)

{

       unregister_shrinker(&lowmem_shrinker);

       task_free_unregister(&task_nb);

}

       当然这里进行初始化和注销工作的前提是先定义shrinker结构体,lowmem_shrink为回调函数的指针,当有内存分页回收的时候,获其他有需要的时机,这个函数将会被调用。

1.2.2阀值表

Android中,存在着一张内存阈值表,这张阈值表是可以在init.rc中进行配置的,合理配置这张表,对于小内存设备有非常重要的作用。我们来看lowmemorykiller.c中这张默认的阈值表:

static intlowmem_adj[6] = {

       0,

       1,

       6,

       12,

};

static intlowmem_adj_size = 4;

static intlowmem_minfree[6] = {

       3 * 512, /*6MB */

       2 * 1024,      /*8MB */

       4 * 1024,      /*16MB */

       16 * 1024,    /* 64MB */

};

lowmem_adj[6]中各项数值代表阈值的警戒级数;lowmem_minfree[6]代表对应级数的剩余内存。也就是说,当系统的可用内存小于6MB时,警戒级数为0;当系统可用内存小于8M而大于6M时,警戒级数为1;当可用内存小于64M大于16MB时,警戒级数为12。Low memory killer的规则就是根据当前系统的可用内存多少来获取当前的警戒级数,如果进程的oom_adj大于警戒级数并且最大,进程将会被杀死(具有相同oom_adj的进程,则杀死占用内存较多的)。oom_adj越小,代表进程越重要。一些前台的进程,oom_adj会比较小,而后台的服务,oom_adj会比较大,所以当内存不足的时候,Low memory killer必然先杀掉的是后台服务而不是前台的进程。

1.2.3回调函数lowmem_shrink

staticint lowmem_shrink(struct shrinker *s, struct shrink_control *sc)

{

   struct task_struct *tsk;

   struct task_struct *selected = NULL;

   int rem = 0;

   int tasksize;

   int i;

   int min_score_adj = OOM_SCORE_ADJ_MAX + 1;

   int selected_tasksize = 0;

   int selected_oom_score_adj;

   int array_size = ARRAY_SIZE(lowmem_adj);

//这部分通过global_page_state()函数获取系统当前可用的内存大小,然后根据可用内存大小及内存阈值表,来计算系统当前的警戒等级。

   int other_free =global_page_state(NR_FREE_PAGES) - totalreserve_pages;

    int other_file =global_page_state(NR_FILE_PAGES) - global_page_state(NR_SHMEM);

 

    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_score_adj = lowmem_adj[i];

            break;

        }

    }

 

 

   if (sc->nr_to_scan > 0)

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

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

            other_file, min_score_adj);

       //计算rem值

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

//如果内存状态还是不错,则返回rem,不做其他操作

   if(sc->nr_to_scan <= 0 || min_score_adj == OOM_SCORE_ADJ_MAX + 1) {

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

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

        returnrem;

    }

   //****下面开始遍历系统中所有的进程,选中将要杀掉的那个进程***

       selected_oom_score_adj= min_score_adj;

 

   rcu_read_lock();

   for_each_process(tsk) {

        struct task_struct *p;

        int oom_score_adj;

 //跳过内核线程,内核线程不参加这个杀进程

        if (tsk->flags & PF_KTHREAD)

            continue;

 

        p = find_lock_task_mm(tsk);//有mm的thread才能操作

        if (!p)

            continue;

 

        if (test_tsk_thread_flag(p, TIF_MEMDIE)&&

          time_before_eq(jiffies,lowmem_deathpending_timeout)) {

            task_unlock(p);

            rcu_read_unlock();

            return 0;

        }

        oom_score_adj =p->signal->oom_score_adj;// 得到task对应的oom_score_adj

       //进程的oom_score_adj小于警戒阈值,则不处理

        if (oom_score_adj < min_score_adj) {

            task_unlock(p);

            continue;

        }

//获取进程所占用的内存大小,RSS值       

tasksize =get_mm_rss(p->mm);

        task_unlock(p);

        if (tasksize <= 0)

            continue;

//如果这个进程的oom_score_adj小于我们已经选中的那个进程的oom_score_adj,或者这个进程的oom_score_adj等于我们已经选中的那个进程的oom_score_adj,但其所占用的内存大小tasksize小于我们已经选中的那个进程所占用内存大小,则继续寻找下一个进程。

        if (selected) {

            if (oom_score_adj <selected_oom_score_adj)

                continue;

            if (oom_score_adj ==selected_oom_score_adj &&

                tasksize <=selected_tasksize)

                continue;

        }

        //选中正在遍历的这个的进程,更新selected_tasksize为这个进程所占用的内存大小tasksize,更新selected_oom_score_adj为这个进程的oom_score_adj。

               selected = p;

        selected_tasksize = tasksize;

        selected_oom_score_adj = oom_score_adj;

        lowmem_print(2, "select %d (%s),adj %d, size %d, to kill\n",

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

    }

 

//*************遍历结束***********************

   //杀死选中的进程。先是更新lowmem_deathpending_timeout,然后便是给进程发送一个signalSIGKILL,接下来是设置进程的标记为TIF_MEMDIE,最后则是更新一下rem的值。

       if (selected) {

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

         selected->pid, selected->comm,

         selected_oom_score_adj,selected_tasksize);

        lowmem_deathpending_timeout = jiffies +HZ;

        send_sig(SIGKILL, selected, 0);

        set_tsk_thread_flag(selected,TIF_MEMDIE);

        rem -= selected_tasksize;

    }

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

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

   rcu_read_unlock();

   return rem;

}

1.2.4 adj阀值设置

       前面提到lowmemorykiller中有一个默认的阈值表。如我们前面所提到的那样,各种各样的设备、产品也可以自己定制这个值。这种定制主要是通过导出一些内核变量来实现的。

#ifdefCONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES

static shortlowmem_oom_adj_to_oom_score_adj(short oom_adj)

{

       if (oom_adj == OOM_ADJUST_MAX)

              return OOM_SCORE_ADJ_MAX;

       else

              return (oom_adj *OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;

}

 

static voidlowmem_autodetect_oom_adj_values(void)

{

       int i;

       short oom_adj;

       short oom_score_adj;

       int array_size = ARRAY_SIZE(lowmem_adj);

 

       if (lowmem_adj_size < array_size)

              array_size = lowmem_adj_size;

 

       if (array_size <= 0)

              return;

 

       oom_adj = lowmem_adj[array_size - 1];

       if (oom_adj > OOM_ADJUST_MAX)

              return;

 

       oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);

       if (oom_score_adj <= OOM_ADJUST_MAX)

              return;

 

       lowmem_print(1, "lowmem_shrink:convert oom_adj to oom_score_adj:\n");

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

              oom_adj = lowmem_adj[i];

              oom_score_adj = lowmem_oom_adj_to_oom_score_adj(oom_adj);

              lowmem_adj[i] = oom_score_adj;

              lowmem_print(1, "oom_adj %d=> oom_score_adj %d\n",

                          oom_adj, oom_score_adj);

       }

}

 

static intlowmem_adj_array_set(const char *val, const struct kernel_param *kp)

{

       int ret;

 

       ret = param_array_ops.set(val, kp);

 

       /* HACK: Autodetect oom_adj values inlowmem_adj array */

       lowmem_autodetect_oom_adj_values();

 

       return ret;

}

 

static intlowmem_adj_array_get(char *buffer, const struct kernel_param *kp)

{

       return param_array_ops.get(buffer, kp);

}

 

static voidlowmem_adj_array_free(void *arg)

{

       param_array_ops.free(arg);

}

 

static structkernel_param_ops lowmem_adj_array_ops = {

       .set = lowmem_adj_array_set,

       .get = lowmem_adj_array_get,

       .free = lowmem_adj_array_free,

};

 

static conststruct kparam_array __param_arr_adj = {

       .max = ARRAY_SIZE(lowmem_adj),

       .num = &lowmem_adj_size,

       .ops = &param_ops_short,

       .elemsize = sizeof(lowmem_adj[0]),

       .elem = lowmem_adj,

};

#endif

 

module_param_named(cost, lowmem_shrinker.seeks, int,S_IRUGO | S_IWUSR);

#ifdefCONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES

__module_param_call(MODULE_PARAM_PREFIX,adj,

                 &lowmem_adj_array_ops,

                 .arr = &__param_arr_adj,

                 S_IRUGO | S_IWUSR, -1);

__MODULE_PARM_TYPE(adj,"array of short");

#else

module_param_array_named(adj, lowmem_adj,short, &lowmem_adj_size,

                      S_IRUGO | S_IWUSR);

#endif

module_param_array_named(minfree, lowmem_minfree,uint, &lowmem_minfree_size,

                      S_IRUGO | S_IWUSR);

module_param_named(debug_level,lowmem_debug_level, uint, S_IRUGO | S_IWUSR);

 

#ifdefCONFIG_ANDROID_LOW_MEMORY_KILLER_AUTODETECT_OOM_ADJ_VALUES

static intdebug_adj_set(const char *val, const struct kernel_param *kp)

{

    const int ret = param_set_uint(val, kp);

    lowmem_debug_adj =lowmem_oom_adj_to_oom_score_adj(lowmem_debug_adj);

    return ret;

}

 

static structkernel_param_ops debug_adj_ops = {

       .set = &debug_adj_set,

       .get = &param_get_uint,

};

 

module_param_cb(debug_adj,&debug_adj_ops, &lowmem_debug_adj, S_IRUGO | S_IWUSR);

__MODULE_PARM_TYPE(debug_adj,short);

#else

module_param_named(debug_adj,lowmem_debug_adj, short, S_IRUGO | S_IWUSR);

#endif

module_param_named(candidate_log,enable_candidate_log, uint, S_IRUGO | S_IWUSR);

       正常情况下,lowmemorykiller在此处总共导出了4个内核变量,其中两个是内核数组,即adjmimfree。这两个就是我们需要关注的。我们可以在手机系统上如下位置访问到这些内核变量:/sys/module/lowmemorykiller/parameters。

       在init.rc文件中会设置这些内核变量的属性(/androidLSoul45/system/core/rootdir/init.rc),以便于后面系统对这些值进行修改。

       on early-init

    # Set init and its forked children'soom_adj.

    write/proc/1/oom_adj -16

       。。。。。。。

 

       # Memory management.  Basic kernel parameters, and allow the high

    # level system server to be able to adjustthe kernel OOM driver

    # parameters to match how it is managingthings.

    write /proc/sys/vm/overcommit_memory 1

    write /proc/sys/vm/min_free_order_shift 4

    chown root system/sys/module/lowmemorykiller/parameters/adj

    chmod 0220/sys/module/lowmemorykiller/parameters/adj

    chown root system/sys/module/lowmemorykiller/parameters/minfree

chmod 0220/sys/module/lowmemorykiller/parameters/minfree

 

。。。。。。。。

将init进程oom_adj设置为-16,从而保证init进程永远不会被杀掉

那通常情况下, adj值又是在什么时候设置的呢?设置这些值的依据是什么,是一个经验值呢还是有什么算法来算出那些值?

写到这里,想起了以前写的《SettingMemmory分类》里面有提到过dumpsysmeminfo –oom进程的映射问题。里面简单提到了进程是在ProcessList.java中根据不同的adj变量来处理,从而最终映射出来的。通过全局搜索整个androidL源码,也发现lowmemorykiller所导出的那些值只在ProcessList.updateOomLevels()方法中被修改。代码位置:

/local/sourcecode/AndroidLSoul45/frameworks/base/services/core/java/com/android/server/am/ProcessList.java现在我们来看看:

final classProcessList {

       //*****进程分类*************//

    // The minimum time we allow betweencrashes, for us to consider this

    // application to be bad and stop and itsservices and reject broadcasts.

    static final int MIN_CRASH_INTERVAL =60*1000;

 

    // OOM adjustments for processes in variousstates:

 

    // Adjustment used in certain places wherewe don't know it yet.

    // (Generally this is something that isgoing to be cached, but we

    // don't know the exact value in the cachedrange to assign yet.)

    static final int UNKNOWN_ADJ = 16;

 

    // This is a process only hostingactivities that are not visible,

    // so it can be killed without anydisruption.

    static final int CACHED_APP_MAX_ADJ = 15;

    static final int CACHED_APP_MIN_ADJ = 9;

 

    // The B list of SERVICE_ADJ -- these arethe old and decrepit

    // services that aren't as shiny andinteresting as the ones in the A list.

    static final int SERVICE_B_ADJ = 8;

 

    // This is the process of the previousapplication 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 twotop recent apps) as well as normal

    // UI flow such as clicking on a URI in thee-mail app to view in the browser,

    // and then pressing back to return toe-mail.

    static final int PREVIOUS_APP_ADJ = 7;

 

    // This is a process holding the homeapplication -- we want to try

    // avoiding killing it, even if it wouldnormally be in the background,

    // because the user interacts with it somuch.

    static final int HOME_APP_ADJ = 6;

 

    // This is a process holding an applicationservice -- killing it will not

    // have much of an impact as far as theuser is concerned.

    static final int SERVICE_ADJ = 5;

 

    // This is a process with a heavy-weightapplication.  It is in the

    // background, but we want to try to avoidkilling it.  Value set in

    // system/rootdir/init.rc on startup.

    static final int HEAVY_WEIGHT_APP_ADJ = 4;

 

    // This is a process currently hosting abackup operation.  Killing it

    // is not entirely fatal but is generally abad idea.

    static final int BACKUP_APP_ADJ = 3;

 

    // This is a process only hostingcomponents that are perceptible to the

    // user, and we really want to avoidkilling them, but they are not

    // immediately visible. An example isbackground music playback.

    static final int PERCEPTIBLE_APP_ADJ = 2;

 

    // This is a process only hostingactivities that are visible to the

    // user, so we'd prefer they don'tdisappear.

    static final int VISIBLE_APP_ADJ = 1;

 

    // This is the process running the currentforeground 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 isnot completely fatal.

    static final int PERSISTENT_PROC_ADJ = -12;

 

    // The system process runs at the defaultadjustment.

    static final int SYSTEM_ADJ = -16;

 

    // Special code for native processes thatare not being managed by the system (so

    // don't have an oom adj assigned by thesystem).

static final intNATIVE_ADJ = -17;

 

//***上面也是android的进程分类。可以从帮助文档可以看出来****//

 

    // Memory pages are 4K.

    static final int PAGE_SIZE = 4*1024;

 

    // The minimum number of cached apps wewant to be able to keep around,

    // without empty apps being able to pushthem out of memory.

    static final int MIN_CACHED_APPS = 2;

 

    //The maximum number of cached processes we will keep around before killing them.

    // NOTE: this constant is *only* a controlto not let us go too crazy with

    // keeping around processes on devices withlarge amounts of RAM.  For devices that

    // are tighter on RAM, the out of memorykiller is responsible for killing background

    // processes as RAM is needed, and weshould *never* be relying on this limit to

    // kill them.  Also note that this limit only applies tocached background processes;

    // we have no limit on the number ofservice, visible, foreground, or other such

    // processes and the number of thoseprocesses does not count against the cached

    // process limit.

    static final int MAX_CACHED_APPS = 32;

 

    // We allow empty processes to stick aroundfor at most 30 minutes.

    static final long MAX_EMPTY_TIME =30*60*1000;

 

    // The maximum number of empty appprocesses we will let sit around.

    private static final int MAX_EMPTY_APPS =computeEmptyProcessLimit(MAX_CACHED_APPS);

 

    // The number of empty apps at which wedon't consider it necessary to do

    // memory trimming.

    static final int TRIM_EMPTY_APPS =MAX_EMPTY_APPS/2;

 

    // The number of cached at which we don'tconsider it necessary to do

    // memory trimming.

    static final int TRIM_CACHED_APPS =(MAX_CACHED_APPS-MAX_EMPTY_APPS)/3;

 

    // Threshold of number of cached+emptywhere we consider memory critical.

    static final int TRIM_CRITICAL_THRESHOLD =3;

 

    // Threshold of number of cached+emptywhere we consider memory critical.

    static final int TRIM_LOW_THRESHOLD = 5;

 

    // Low Memory Killer Daemon command codes.

    // These must be kept in sync with thedefinitions in lmkd.c

    //

    // LMK_TARGET <minfree> <minkillprio>... (up to 6 pairs)

    // LMK_PROCPRIO <pid> <prio>

    // LMK_PROCREMOVE <pid>

    static final byte LMK_TARGET = 0;

    static final byte LMK_PROCPRIO = 1;

    static final byte LMK_PROCREMOVE = 2;

 

    // These are the various interesting memorylevels that we will give to

    // the OOM killer.  Note that the OOM killer only supports 6slots, so we

    // can't give it a different value forevery possible kind of process.

    private int[] mOomAdj = new int[] {

            FOREGROUND_APP_ADJ,VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,

            BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ,CACHED_APP_MAX_ADJ

    };

    // These are the low-end OOM levellimits.  This is appropriate for an

    // HVGA or smaller phone with less than512MB.  Values are in KB.

    private final int[] mOomMinFreeLow = newint[] {

            8192, 12288, 16384,

            24576, 28672, 32768

    };

    // These are the high-end OOM levellimits.  This is appropriate for a

    // 1280x800 or larger screen with around1GB RAM.  Values are in KB.

    private final int[] mOomMinFreeHigh = newint[] {

            49152, 61440, 73728,

            86016, 98304, 122880

    };

    // The actual OOM killer memory levels weare using.

    private int[] mOomMinFree = newint[mOomAdj.length];

 

    private final long mTotalMemMb;

 

    private long mCachedRestoreLevel;

 

    private boolean mHaveDisplaySize;

 

    private static LocalSocket sLmkdSocket;

    private static OutputStream sLmkdOutputStream;

 

    ProcessList() {

        MemInfoReader minfo = newMemInfoReader();

        minfo.readMemInfo();

        mTotalMemMb =minfo.getTotalSize()/(1024*1024);

        updateOomLevels(0, 0, false);

    }

 

    void applyDisplaySize(WindowManagerServicewm) {

        if (!mHaveDisplaySize) {

            Point p = new Point();

           wm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, p);

            if (p.x != 0 && p.y != 0) {

                updateOomLevels(p.x, p.y,true);

                mHaveDisplaySize = true;

            }

        }

    }

 

      private void updateOomLevels(int displayWidth, int displayHeight, boolean write){

        /// M: LCA: minfree only depends onmemory size @ {

        if (SystemProperties.get("ro.mtk_lca_ram_optimize").equals("1")){

            // in LCA, do not update the lmkminfree value, read the value to java part

            String adj =readFile("/sys/module/lowmemorykiller/parameters/adj");

            String minfree =readFile("/sys/module/lowmemorykiller/parameters/minfree");

            String[] adjs =adj.split(",");

            String[] minfrees =minfree.split(",");

            try {

                mOomAdj = new int[adjs.length];

                mOomMinFree = newint[adjs.length];

                for (int i = 0; i <adjs.length; ++i) {

                    mOomAdj[i] =Integer.parseInt(adjs[i]);

                   Log.i(ActivityManagerService.TAG, "adj: " + mOomAdj[i]);

                }

                for (int i = 0; i <adjs.length; ++i) {

                    mOomMinFree[i] =Integer.parseInt(minfrees[i]) * PAGE_SIZE / 1024;

                   Log.i(ActivityManagerService.TAG, "minfree: " +mOomMinFree[i]);

                }

            } catch (Exception e) {

                Slog.w(ActivityManagerService.TAG,"parse lmk adj,minfree failed");

            }

            return;

        }

        /// @}

 

        // Scale buckets from avail memory: at300MB we use the lowest values to

        // 700MB or more for the top values.

              //计算scale

        float scaleMem =((float)(mTotalMemMb-300))/(700-300);

 

        // Scale buckets fromscreen size.

        int minSize =480*800;  //  384000

        int maxSize =1280*800; // 1024000  230400 870400  .264

        float scaleDisp =((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);

        if (false) {

           Slog.i("XXXXXX", "scaleMem=" + scaleMem);

           Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + "dw=" + displayWidth

                    + "dh=" + displayHeight);

        }

 

        float scale = scaleMem> scaleDisp ? scaleMem : scaleDisp;

 

 

        if (scale < 0) scale = 0;

        else if (scale > 1) scale = 1;

        int minfree_adj =Resources.getSystem().getInteger(

               com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAdjust);

        int minfree_abs =Resources.getSystem().getInteger(

               com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);

        if (false) {

            Slog.i("XXXXXX","minfree_adj=" + minfree_adj + " minfree_abs=" +minfree_abs);

        }

 

        final boolean is64bit =Build.SUPPORTED_64_BIT_ABIS.length > 0;

 

 

//第一轮计算各个adj所对应的minimumfree memory阈值。计算各个值的算式为(long)(low+ ((high-low)*scale))。low值和high值都是预定义的固定的经验值,比较关键的是那个scale值。在前面计算scale的部分,我们可以看到,它会先计算一个memory的scale值(为((float)(mTotalMemMb-300))/(700-300)),再计算一个屏幕分辨率的scale值

(((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize)),

首先取scale值为这两个scale值中较大的那一个。再然后是一些容错处理,将scale值限制在0~1之间,以防止设备内存小于300MB,同时设备分辨率小于480*800;或者,设备内存大于700MB,或设备分辨率大于1280*800的情况出现时,出现太不合理的阈值。

        for (int i=0; i<mOomAdj.length; i++){

            int low = mOomMinFreeLow[i];

            int high = mOomMinFreeHigh[i];

            mOomMinFree[i] = (int)(low +((high-low)*scale));

            if (is64bit) {

                // On 64 bit devices, weconsume more baseline RAM, because 64 bit is cool!

                // To avoid being all pagey andstuff, scale up the memory levels to

                // give us some breathing room.

                mOomMinFree[i] =(3*mOomMinFree[i])/2;

            }

        }

 

//第二轮计算各个adj所对应的minifree memory阈值。计算各个值的算式为(long)((float)minfree_abs* mOomMinFree[i] / mOomMinFree[mOomAdj.length - 1])。此处给了各设备对lowmemory阈值进行定制的机会。各个设备可以在framework的config文件中定义config_lowMemoryKillerMinFreeKbytesAbsolute,以指定最大的adj所对应的freememory阈值,其他各个adj所对应的freememory阈值将依比例算出。config.xml在: AndroidLSoul45/frameworks/base/core/res/res/values/config.xml。

 

        if (minfree_abs >= 0) {

            for (int i=0; i<mOomAdj.length;i++) {

                mOomMinFree[i] =(int)((float)minfree_abs * mOomMinFree[i]

                        /mOomMinFree[mOomAdj.length - 1]);

            }

        }

 

//第三轮计算各个adj所对应的minifree memory阈值。计算各个值的算式为mOomMinFree[i]+= (long)((float)minfree_adj * mOomMinFree[i] / mOomMinFree[mOomAdj.length -1])。此处是给特定设备微调lowmemory的阈值提供机会。不过我们仔细来看第二轮和第三轮的计算,这两轮计算似乎是可以合并为:(long)((float)(minfree_abs+ minfree_adj)  * mOomMinFree[i] / mOomMinFree[mOomAdj.length- 1])。

        if (minfree_adj != 0) {

            for (int i=0; i<mOomAdj.length;i++) {

                mOomMinFree[i] +=(int)((float)minfree_adj * mOomMinFree[i]

                        /mOomMinFree[mOomAdj.length - 1]);

                if (mOomMinFree[i] < 0) {

                    mOomMinFree[i] = 0;

                }

            }

        }

 

        // The maximum size we will restore aprocess from cached to background, when under

        // memory duress, is 1/3 the size wehave reserved for kernel caches and other overhead

        // before killing background processes.

        mCachedRestoreLevel =(getMemLevel(ProcessList.CACHED_APP_MAX_ADJ)/1024) / 3;

 

        // Ask the kernel to try to keep enoughmemory free to allocate 3 full

        // screen 32bpp buffers withoutentering direct reclaim.

        int reserve = displayWidth *displayHeight * 4 * 3 / 1024;

        int reserve_adj =Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAdjust);

        int reserve_abs =Resources.getSystem().getInteger(com.android.internal.R.integer.config_extraFreeKbytesAbsolute);

 

        if (reserve_abs >= 0) {

            reserve = reserve_abs;

        }

 

        if (reserve_adj != 0) {

            reserve += reserve_adj;

            if (reserve < 0) {

                reserve = 0;

            }

        }

 

//由前面构造适合设置给lowmemorykiller导出参数的字符串,并最终将构造的这组参数设置进去。

        if (write) {

            ByteBuffer buf =ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));

            buf.putInt(LMK_TARGET);

            for (int i=0; i<mOomAdj.length;i++) {

               buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);

                buf.putInt(mOomAdj[i]);

            }

 

            writeLmkd(buf);

            SystemProperties.set("sys.sysctl.extra_free_kbytes",Integer.toString(reserve));

        }

        // GB: 2048,3072,4096,6144,7168,8192

        // HC:8192,10240,12288,14336,16384,20480

    }

adj值是一组写死的固定的值,具体可以参考mOomAdj的定义。最后面如果是android4.4的话,则为:

//Slog.i("XXXXXXX","******************************* MINFREE: " + memString);

        if (write) {

           writeFile("/sys/module/lowmemorykiller/parameters/adj",adjString.toString());

           writeFile("/sys/module/lowmemorykiller/parameters/minfree",memString.toString());

           SystemProperties.set("sys.sysctl.extra_free_kbytes",Integer.toString(reserve));

        }

       这里还有一个问题,updateOomLevels()在那里使用?首先在前面的applyDisplaySize()方法中使用。在这个方法中,当能够从WMS中获取有效的屏幕分辨率时,会去更新oomlevels,并且更新之后就不会再次去更新。

       通过搜索发现,只有在ActivityManagerService中的updateConfiguration()方法中调用了。具体看看:

public voidupdateConfiguration(Configuration values) {

       enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,

               "updateConfiguration()");

 

        synchronized(this) {

            if (values == null &&mWindowManager != null) {

                // sentinel: fetch the currentconfiguration from the window manager

                values =mWindowManager.computeNewConfiguration();

            }

 

            if (mWindowManager !=null) {

               mProcessList.applyDisplaySize(mWindowManager);

            }

 

            final long origId =Binder.clearCallingIdentity();

            if (values != null) {

               Settings.System.clearConfiguration(values);

            }

            updateConfigurationLocked(values,null, false, false);

           Binder.restoreCallingIdentity(origId);

        }

    }

这里总结一下OOMlevels的更新:更新是在系统启动后,第一个configurationchange的消息到AMS的时候。adj值为一组固定的预定义的值;各个adj所对应的minfree阈值则根据系统的内存大小和屏幕的分辨率计算得出,各个设备还可以通过framework的configconfig_lowMemoryKillerMinFreeKbytesAdjust

和config_lowMemoryKillerMinFreeKbytesAbsolute来定制最终各个adj所对应的minfree阈值。

这里还要提一下Process是怎么set adj值的。

通过Process.setOomAdj(intpid, int amt)进行设置,其中在ActivityManagerService中updateOomAdjLocked()函数中就有调用。它由android_util_Process.cpp实现该函数。

Process.java:

public static final native boolean setOomAdj(int pid,int amt);

 

android_util_Process.cpp:

jboolean android_os_Process_setOomAdj(JNIEnv* env,jobject clazz,

                                      jint pid,jint adj)

{

#ifdef HAVE_OOM_ADJ

    chartext[64];

    sprintf(text, "/proc/%d/oom_adj",pid);

    int fd =open(text, O_WRONLY);

    if (fd >=0) {

        sprintf(text, "%d", adj);

       write(fd, text, strlen(text));

       close(fd);

    }

    return true;

#endif

    returnfalse;

}

1.3 low memory killer的触发

1.3.1 adj变化

         我们先从手机上打印出adj设置值信息:

Pixi335:

user@swd2:/local/sourcecode/$ adb shell cat /sys/module/lowmemorykiller/parameters/adj

0,58,117,235,352,470,529,705,882

 

user@swd2:/local/sourcecode/$ adb shell cat /sys/module/lowmemorykiller/parameters/minfree

1024,3072,5120,6144,7168,8192,9216,10240,12288

       如上文得知,lowmemorykiller.c导出了一些内核参数,以便于用户空间的程序可以对这些值进行重新配置。实际上,在system_server的AMS里,系统启动之后会根据系统所运行的具体状况,如屏幕分辨率,内存大小等,重新计算这些值,并设置给内核。结论也就是:lowmemorykillerdriver代码里面定义的那些值会被AMS给更改掉。

       可以看lowmemorykiller.c里面lowmem_adj_array_set()这个函数,这个函数应该是在system_server设置adj值的时候会被调用到。这个方法里面调用了lowmem_autodetect_oom_adj_values(),在lowmem_autodetect_oom_adj_values()这个函数里面会再对oom_adj值做一些调整。那最后也就是说:由于内核导出了一些参数方便我们设置,所以在/sys/module/lowmemorykiller/parameters/adj里面设置的参数(0、1、2、4、6、8、9、12、15)会在系统启动时设置给内核,由于此时内核中对adj对应的数组lowmem_adj进行了重新赋值(计算之后的oom_score_adj),计算之后的数值也会同步给/sys/module/lowmemorykiller/parameters/adj这个文件。这个是module_param_array_named()这个函数的功能?

       网上查了一下:sys 下的东西就是内核的东西。/sys/module/lowmemorykiller/parameters/adj这个里面的值,是在kerneldriver被加载进系统时,会是kernel里面预定义的那组值。在system_server设置那个值,也就是写sys文件的时候,给的值是类似于(0、1、2、4、6、8、9、12、15)这样的一组值。在写sys文件时,内核里面会有一些callback被调用到,就是lowmem_adj_array_set()。在这个函数里,会对system_server设置的(0、1、2、4、6、8、9、12、15)这样的一组值再做一个处理,就是最后看到的那组值。

1.3.2 LWK触发

       low memory killer究竟是怎么执行起来的呢?不难猜测lowmemory killer的执行应该是在系统内存不足的时候。那lowmemory killer会在什么样的进程中执行呢?我们来构造一个系统memory 不足的场景:创建几个简单app,在每个app中都分配尽可能多的内存(分配内存过多进程会发生OutOfMemoryError并退出),如下面这样:

public classMainActivity extends Activity {

 

       int[] data;

       @Override

       protected void onCreate(BundlesavedInstanceState) {

              super.onCreate(savedInstanceState);

              setContentView(R.layout.activity_main);

 

              data = new int[20 * 1024 *1024];

        for (int i = 0; i < data.length; ++i) {

            data[i] = i;

        }

       }

每一个app(如:out1)会分配80M的内存,让类似这样的几个app在设备上执行起来。然后我们将可以看到内核吐出来的log:

out1、out2、out3、out4都被kill掉,lowmemorykiller运行起来咯,也就是lowmemorykiller.c这段代码运行吐出来的。

if (selected) {

              lowmem_print(1, "Killing '%s' (%d), adj %d, score_adj%hd,\n" \

                            "   to free %ldkB on behalf of '%s' (%d)because\n" \

                            "   cache %ldkB is below limit %ldkB foroom_score_adj %hd\n" \

                            "   Free memory is %ldkB above reserved\n",

                          selected->comm, selected->pid,

                             REVERT_ADJ(selected_oom_score_adj),

                          selected_oom_score_adj,

                          selected_tasksize * (long)(PAGE_SIZE /1024),

                          current->comm, current->pid,

                          other_file * (long)(PAGE_SIZE / 1024),

                          minfree * (long)(PAGE_SIZE / 1024),

                          min_score_adj,

                          other_free * (long)(PAGE_SIZE / 1024));

              lowmem_deathpending_timeout =jiffies + HZ;

       仔细观察log截图,发现执行了lowmemorykiller的进程总共有3个:内核线程kswapd0native app adbdapp com.example.outX。由此,我们推测lowmemorykiller大概有两种执行方式:

(1)是在kswapd0中,kswapd内核线程会被定期唤醒,当它发现系统内存不足的时候,它就会去执行lowmemorykiller杀进程,并回收内存。

(2)是在app中,当一个app请求分配内存,而系统又没有足够的内存可供分配时,则分配内存的请求将会先被阻塞,执行lowmemorykiller杀进程,回收内存之后再尝试着去分配内存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值