low memory killer

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,比如对于android L,代码在:/local/sourcecode/AndroidLSoul45/kernel-3.10/drivers/staging/android/lowmemorykiller.c。
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来释放内存。
结构体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 void register_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 int lowmem_adj[6] = {
0,
1,
6,
12,
};

static int lowmem_adj_size = 4;

static int lowmem_minfree[6] = {
3 * 512, /*6MB */ //一页为4K
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
static int 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个内核变量,其中两个是内核数组,即adj和mimfree。这两个就是我们需要关注的。我们可以在手机系统上如下位置访问到这些内核变量:/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,同时设备分辨率小于480800;或者,设备内存大于700MB,或设备分辨率大于1280800的情况出现时,出现太不合理的阈值。

    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 class MainActivity 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个:内核线程kswapd0、native app adbd和app com.example.outX。由此,我们推测lowmemorykiller大概有两种执行方式:

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

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

参考文件:
https://blog.csdn.net/zhouzhengting1/article/details/48595821
1.3.3 ADJ级别

从Android 7.0开始,ADJ采用100、200、300;在这之前的版本ADJ采用数字1、2、3,这样的调整可以更进一步地细化进程的优先级,比如在VISIBLE_APP_ADJ(100)与PERCEPTIBLE_APP_ADJ(200)之间,可以有ADJ=101、102级别的进程。

省去lmk对oom_score_adj的计算过程,Android 7.0之前的版本,oom_score_adj= oom_adj * 1000/17; 而Android 7.0开始,oom_score_adj= oom_adj,不用再经过一次转换。

参考
http://gityuan.com/2018/05/19/android-process-adj/

1.4内存
1.4.1系统memory 可简单分成以下几类
user space memory
anon (process memory , stack , heap ,…)
file (file cache)
swap (zipped anon)
kernel space memory
slab_reclaimable
slab_unreclaimable
kernel_stack
ion (mutimedia/camera)
gpu (display/graphic)
1.4.2查看内存分布
adb cat /proc/meminfo
MemTotal: 1876772 kB //系统可用的总内存 (MemTotal = real_Dram_size - hw_reserved - kernel_reserved )
MemFree: 46304 kB //系统剩余尚未使用的内存
MemAvailable: 424492 kB //系统可用内存 , 包含已使用但可回收的 , 如cache/buffer、slab一部分
Buffers: 6988 kB //用于文件缓冲
Cached: 393952 kB //用于文件高速缓存,约等于 Active(file) + Inactive(file)
SwapCached: 5312 kB //用于swap缓存
Active: 446960 kB //活跃使用状态,记录最近使用过的内存,通常不回收用于其它目的
Inactive: 454008 kB //非活跃使用状态,记录最近并没有使用过的内存,能够被回收用于其他目的
Active(anon): 249988 kB //Active = Active(anon) + Active(file)
Inactive(anon): 255448 kB //Inactive = Inactive(anon) + Inactive(file)
Active(file): 196972 kB
Inactive(file): 198560 kB
Unevictable: 5580 kB
Mlocked: 5580 kB
SwapTotal: 1048572 kB //swap总大小
SwapFree: 57300 kB //swap可用大小
Dirty: 164 kB //等待往磁盘回写的大小
Writeback: 0 kB //正在往磁盘回写的大小
AnonPages: 504068 kB //匿名页,用户空间的页表,没有对应的文件
Mapped: 305784 kB //文件通过mmap分配的内存,用于map设备、文件或者库
Shmem: 516 kB
Slab: 194932 kB //kernel数据结构的缓存大小,Slab=SReclaimable+SUnreclaim
SReclaimable: 39936 kB //可回收的slab的大小
SUnreclaim: 154996 kB //不可回收slab的大小
KernelStack: 69840 kB
PageTables: 99908 kB //以最低的页表级
NFS_Unstable: 0 kB //不稳定页表的大小
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 1986956 kB
Committed_AS: 134795476 kB
VmallocTotal: 263061440 kB //总分配的虚拟地址空间
VmallocUsed: 0 kB //已使用的虚拟地址空间
VmallocChunk: 0 kB //虚拟地址空间可用的最大连续内存块
CmaTotal: 0 kB
CmaFree: 0 kB
1.4.3内存分布log 查看方式
kernel log

<4>[366310.867958] DMA free:68160kB min:5140kB low:44156kB high:45996kB active_anon:126592kB inactive_anon:126772kB active_file:211508kB inactive_file:185592kB unevictable:5380kB writepending:1896kB present:1988536kB managed:1841596kB mlocked:5380kB pagetables:78368kslab_reclaimable:40648kB slab_unreclaimable:187980kB kernel_stack:49760kBB bounce:0kB free_pcp:2844kB local_pcp:696kB free_cma:0kB

DMA free:68160kB
    总剩余内存
min:5140kB low:44156kB high:45996kB 
    min/low/high 三个水位值
active_anon:126592kB inactive_anon:126772kB
    userspace process memory
active_file:211508kB inactive_file:185592kB
    userspace file cache memory
managed:1841596kB
    dram total减掉 reserved memory ,系统总共可运用的内存大小
slab_reclaimable:40648kB slab_unreclaimable:187980kB
    slab 占用内存
kernel_stack:49760kB
    kernel stack 占用内存, 可用来计算process数量 (32bit 8k per process , 64bit 16k per porcess)

注意ion 和 gpu 无法由此看出

1.4.4 lowmemory killer log查看方式
<4>[366675.752164] (5)[138:kswapd0:0]3038 pages in swap cache
<4>[366675.752181] (5)[138:kswapd0:0]Swap cache stats: add 901120852, delete 901117814, find 143707351/518237855
<4>[366675.752186] (5)[138:kswapd0:0]Free swap = 6700kB //剩余swap 大小
<4>[366675.752191] (5)[138:kswapd0:0]Total swap = 1310716kB //总swap分区大小
<4>[366675.752197] (5)[138:kswapd0:0]497134 pages RAM
<4>[366675.752201] (5)[138:kswapd0:0]0 pages HighMem/MovableOnly
<4>[366675.752207] (5)[138:kswapd0:0]36735 pages reserved
<4>[366675.752224] (5)[138:kswapd0:0]0 pages cma reserved
<4>[366675.752229] (5)[138:kswapd0:0]19548 pages ion total used
<6>[366675.770473] (3)[139:kswapd0:1]lowmemorykiller: Killing ‘.player:service’ (12457) (tgid 12457), adj 800,
<6>[366675.770473] to free 19796kB on behalf of ‘kswapd0:1’ (139) because
<6>[366675.770473] cache 190580kB is below limit 294912kB for oom_score_adj 300 (300)
<6>[366675.770473] Free memory is 4396kB above reserved(decrease 2 level)

lowmemorykiller log 说明
    由kswapd0 进程进行内存回收时触发lowmemory killer机制杀进程
    .player:service 为被杀进程  , 对应 pid=12457 , tid=12457 , adj=800
    释放 19796 KB 内存
    decrease 2 level 表示mtk AMR (aggressive memory relaim 机制,根据swap free/swap total ,  <1/2 降 1 , <1/4 降2) , 当下swap <1/4 所以降 2 level
    当下cache大小  190580kB    低于  294912kB , oom adj =906  -> 900 -> 300 (降2 level) 
    oom adj  参考/sys/module/lowmemorykiller/parameters/adj
    oom minfree 参考/sys/module/lowmemorykiller/parameters/minfree

1.4.5基本debug指令
查看系统memory 使用情况
adb shell cat /proc/meminfo
adb shell dumpsys meminfo
adb shell procrank

查看单一进程memory 使用情况

adb shell dumpsys meminfo $pid
adb shell procmem $pid

最后,给广大应用开发者一些友好的建议:

1. UI进程与Service进程一定要分离,因为对于包含activity的service进程,一旦进入后台就成为”cch-started-ui-services”类型的cache进程(ADJ>=900),随时可能会被系统回收;而分离后的Service进程服务属于SERVICE_ADJ(500),被杀的可能性相对较小。尤其是系统允许自启动的服务进程必须做UI分离,避免消耗系统较大内存。
2. 只有真正需要用户可感知的应用,才调用startForegroundService()方法来启动前台服务,此时ADJ=PERCEPTIBLE_APP_ADJ(200),常驻内存,并且会在通知栏常驻通知提醒用户,比如音乐播放,地图导航。切勿为了常驻而滥用前台服务,这会严重影响用户体验。
3. 进程中的Service工作完成后,务必主动调用stopService或stopSelf来停止服务,避免占据内存,浪费系统资源;
4. 不要长时间绑定其他进程的service或者provider,每次使用完成后应立刻释放,避免其他进程常驻于内存;
5. APP应该实现接口onTrimMemory()和onLowMemory(),根据TrimLevel适当地将非必须内存在回调方法中加以释放。当系统内存紧张时会回调该接口,减少系统卡顿与杀进程频次。
6. 减少在保活上花心思,更应该在优化内存上下功夫,因为在相同ADJ级别的情况下,系统会选择优先杀内存占用的进程。
发布了46 篇原创文章 · 获赞 0 · 访问量 646

猜你喜欢

转载自blog.csdn.net/qq_42894864/article/details/103952059
今日推荐