Android oom机制浅析

本文主要基于android11,整理了android关于 oom killer, lowmemorykiller(lmk lmkd), kswapd的知识,还未深入去分析源码。

目录

linux oom

oom_adj、oom_score、oom_score_adj三者的关系

android low memory killer(lmk)

android app oom

kswapd

zRAM

kswapd 和lmkd

总结


linux oom

  • 杀进程规则:按优先级,/proc/pid/oom_score(由oom_score_adj算出) 这个值越大,越容易被oom killer选中,
  • linux oom 机制是做在kernel里边
  • 为了避免自己的进程比较容易被杀,用户可以通过调低 oom_score_adj这个值。若直接 echo -17 > /proc/$PID/oom_adj,则可以让oom killer 忽略该进程。
  • 具体实现机制在这oom_kill.c
  • linux oom 是针对整个系统而言的,触发oom是在分配内存的时候(alloc_page(),分配内存肯定会走这个,内存都是按页管理),发现内存不足,触发oom killer,算出最应该杀的那些进程,杀了后回收内存(具体是杀一个还是杀好几个还没了解)。

oom_adj、oom_score、oom_score_adj三者的关系

  • oom_score: kernel根据oom_score_adj值,结合该进程使用的内存量算出的值,后面无论是oom killer还是lmkd都是根据这个值来决定杀哪些进程的。
  • oom_score_adj: 取值为-1000---1000,如果赋值为-1000将关闭oom killer对他的管理(如init、surfaceflinger等native 进程)。它和oom_adj都是kernel留给用户空间更改进程被杀优先级的接口,AMS调整app adj值就是通过把pid、uid、 adj 封装成 LMK_PROCPRIO 的消息包通过unix socket传给lmkd,通过lmkd改了这个值。
//frameworks/base/services/core/java/com/android/server/am/processlist.java
 public static void setOomAdj(int pid, int uid, int amt) {
        //...
        ByteBuffer buf = ByteBuffer.allocate(4 * 4);
        buf.putInt(LMK_PROCPRIO);
        buf.putInt(pid);
        buf.putInt(uid);
        buf.putInt(amt);
        writeLmkd(buf, null);//通过unix socket写给lmkd
        //...
    }
//system/memory/lmkd/lmkd.cpp
static void ctrl_command_handler(int dsock_idx) {
    //...
        case LMK_PROCPRIO:
        cmd_procprio(packet, nargs, &cred);
        break;
    //...
}

static void cmd_procprio(LMKD_CTRL_PACKET packet, int field_count, struct ucred *cred) {
    //...
    snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);
    snprintf(val, sizeof(val), "%d", params.oomadj);//ams传过来的adj值
    if (!writefilestring(path, val, false)) {
        return;
    }

    //...
}
  • oom_adj:是oom_score_adj之前的老接口,为了兼容而保留,取值为-16--15,如果赋值为-17将关闭oom killer对他的管理。现在如果用户改变这个值,后面在kernel里边也会换算成oom_score_adj。
//kernel/msm-4.19/Documentation/filesystems/proc.txt

For backwards compatibility with previous kernels, /proc/<pid>/oom_adj may also
be used to tune the badness score.  Its acceptable values range from -16
(OOM_ADJUST_MIN) to +15 (OOM_ADJUST_MAX) and a special value of -17
(OOM_DISABLE) to disable oom killing entirely for that task.  Its value is
scaled linearly with /proc/<pid>/oom_score_adj.

android low memory killer(lmk)

  • android 主要是使用用户空间的lmkd来管理,主要是面向app的内存管理进程(一般系统进程oom_adj都小于0, lmkd只杀oom_adj >= 0的进程),也是利用了oom_adj、oom_score_adj来算oom_score分数进而决定要杀的进程,只不过加了进程分级和定时检测的机制,每个等级有自己的门限值,触发之后就开始按等级杀一批进程。
  • 什么时候用到oom killer?一直用不到。
  • 对比 linux oom killer, android 里边生效的是lmkd,linux oom的代码android有编进kernel,不过不会触发,始终是lmkd先触发(还不知为何,是因为lmkd门限比oom killer低的缘故?)。lmkd是针对整个系统门限阀值来的,低于某个阀值触发lmk,有自己的独立进程 lmkd,而linux oom是针对内存分配不够时才触发。
  • oom 内存警戒表

  • 这个表是定义了一个对应关系,每一个警戒值对应了一个重要性值,当系统的可用内存低于某个警戒值时,就杀掉所有大于该警戒值对应的重要性值的程序。 比如说,当可用内存小于6144 * 4K = 24MB时,开始杀所有的adj为15的进程,当可用内存小于5632 * 4K = 22MB时,开始杀所有adj为14的进程。
  • android AMS会根据app的状态,LRU机制来更改app的oom_score_adj值,进而影响app在lmkd里边的等级和被杀的优先级。

android app oom

为了维持多任务环境的正常运行,Android 会为每个应用的堆大小设置硬性上限。不同设备的确切堆大小上限取决于设备的总体可用 RAM 大小。如果您的应用在达到堆容量上限后尝试分配更多内存,则可能会收到 OutOfMemoryError

引自:https://developer.android.com/topic/performance/memory-overview?hl=zh-cn

kswapd

对比linux 传统kswapd拿磁盘做swap内存用,将内存内容换到磁盘;

android kswapd, 拿内存(zRAM)做swap内存用,多了压缩和解压 (其实也是源自linux,只是android固定有在用,我看有的linux有用而有的没有,还有zSwap, zCacha啥的,linux那些还没有详细了解)。

内存不足时,优先触发 kswapd,将干净页的内容删了释放内存,将脏页内容压缩后写到 zRAM,如果和脏页关联的进程退出,zRAM将会释放,若在这之后内存还不够用,就触发low memory killer,

图片来自:https://developer.android.com/topic/performance/memory-management?hl=zh-cn#kswapd

zRAM

zRAM硬件上也是用了内存条,用来实现类似于swap的功能,由kswapd管理,数据进出zRAM时会进行压缩和解压缩,zRAM空间可以根据数据量的大小动态伸缩,也可以通过 /sys/block/zram0/disksize 进行查看和配置。本质就是拿时间换空间,拿cpu的解压和压缩换取更多的内存使用空间(具体咋压的那些压缩算法还不了解)。

zRAM是做成块设备的形式,驱动代码在kernel/msm-4.19/drivers/block/zram/zram_drv.c, 创建了块设备,提供了io接口,开机时由驱动划分出一块实体硬件内存使用,用户空间通过 /sys/block/zram0 与zRAM交互,

kswapd 和lmkd

内存不够用时,先触发kswapd进行内存压缩,比如高通骁龙888拿4GB做swap,4GB用到一定程度后(这个程度不固定,还没理清为何,拿8GB的高通骁龙888手机试了几次,有时是4GB用完有时是用2GB有时是1GB),lmkd就开始杀进程了。

总结

总体来看,对于android 来说,管理内存不足有2个服务,kswapd和lmkd, 内存不足时,先触发kswapd,将干净页的内存内容删除或将脏页内存内容压缩写入ZRam然后释放内存,若还不够用,触发lmkd,根据oom_score来决定杀哪些app进程,而AMS会根据app状态结合lru机制通过lmkd更改app的oom_score_adj进而影响app被lmkd杀的优先级。

参考:

https://blog.csdn.net/jun5753/article/details/105670683

https://jekton.github.io/2019/03/21/android9-lmk-lmkd/

https://blog.csdn.net/johnWcheung/article/details/87600413

https://developer.android.com/topic/performance/memory-management?hl=zh-cn#kswapd

https://kernel.meizu.com/zram-introduction.html

http://www.wowotech.net/memory_management/zram.html

https://developer.android.com/topic/performance/memory-overview?hl=zh-cn

kernel/msm-4.19/Documentation/filesystems/proc.txt

猜你喜欢

转载自blog.csdn.net/goodnight1994/article/details/118556198