OOM和adj值的区分

来源:互联网 发布:淘宝代销下单流程 编辑:程序博客网 时间:2024/05/21 18:50

内存管理模块——lowmemory killer和out-of-memory killer

1.文章概述

在项目debug时发现log中经常不断的触发lowemeory killer(以后简称LMK)机制去kill掉一些进程,后查明是在sharefolder时候配置init.qcom.post_boot.sh对LMK门限值设定出现遗漏,导致阀值过高,以至于LMK长期被触发,并占用了大量内存系统资源,浪费进程空间。让上层同事对此造成误会,特对此Memorykill做出简要描述。

2.LMK

Android Kernel会定时执行一次对内存的检查,如果达到一定阀值,则杀死一些进程,释放其内存,采用的就是Low memory kille机制。

LMK在计算kill进程时主要是通过进程的oom_adj值来判定进程的重要程度。这个值越小,程序越重要,被杀的可能性越低。

Low memory killer源码和具体实现可参看:kernel/drivers/misc/lowmemorykiller.c

2.1 LMK逻辑流程:
a.LMK开始工作时,首先根据阈值表确定当前的警戒级数,则高于警戒级数的进程是待杀的范围。
b.然后遍历所有进程的oom_adj值,找到大于min_adj的进程,若找到多个,则把占用进程最大的进程存放在selected中。 
c.最后也是关键的一步就是,发送SIGKILL信息,杀掉该进程。

2.2 OOM和adj值的区分

oom_adj的大小和进程的类型以及进程被调度的次序有关。 

Oom_adj在对进程类型进行分类的规则,可以在ActivityManagerService中清楚的看到: 

3.     staticfinal int EMPTY_APP_ADJ;

4.     staticfinal int HIDDEN_APP_MAX_ADJ;

5.     staticfinal int HIDDEN_APP_MIN_ADJ;

6.     staticfinal int HOME_APP_ADJ;

7.     staticfinal int BACKUP_APP_ADJ;

8.     staticfinal int SECONDARY_SERVER_ADJ;

9.     staticfinal int HEAVY_WEIGHT_APP_ADJ;

10.    staticfinal int PERCEPTIBLE_APP_ADJ;

11.    staticfinal int VISIBLE_APP_ADJ;

12.    staticfinal int FOREGROUND_APP_ADJ;

13.    staticfinal int CORE_SERVER_ADJ = -12;

14.    staticfinal int SYSTEM_ADJ = -16; 

ActivityManagerService定义各种进程的oom_adj,CORE_SERVER_ADJ代表一些核心的服务的omm_adj,数值为-12,这类进程永远也不会被杀死(异常crash除外)。

其他未赋值的都在static块中进行了初始化,是通过system/rootdir/init.rc进行配置的,其配置信息如下:

在init.rc中:

15.# Define theoom_adj values for the classes of processes that can be

16.# killed by thekernel.  These are used in ActivityManagerService.

17.   setpropro.FOREGROUND_APP_ADJ 0

18.   setpropro.VISIBLE_APP_ADJ 1

19.   setpropro.SECONDARY_SERVER_ADJ 2

20.   setpropro.HIDDEN_APP_MIN_ADJ 7

21.   setpropro.CONTENT_PROVIDER_ADJ 14

22.   setpropro.EMPTY_APP_ADJ 15

23. 

24.# Define the memorythresholds at which the above process classes will

25.# bekilled.  These numbers are in pages (4k).

26.   setpropro.FOREGROUND_APP_MEM 1536

27.   setpropro.VISIBLE_APP_MEM 2048

28.   setpropro.SECONDARY_SERVER_MEM 4096

29.   setpropro.HIDDEN_APP_MEM 5120

30.   setpropro.CONTENT_PROVIDER_MEM 5632

31.   setpropro.EMPTY_APP_MEM 6144

32. 

而设计到LMK的配置文件有如下三个:

a) /sys/module/lowmemorykiller/parameters/adj

b) /sys/module/lowmemorykiller/parameters/minfree

c)  Init.qcom.post_boot.sh

owmeme_adj:中各项数值代表阈值的警戒级数,

lowmem_minfree:代表对应级数的剩余内存。

Init.qcom.post_boot.sh:中可以配置Lmk不同阶段阀值大小。该文件适用于整机优化内容。而对于某些小内存设备,也可以手动调整对应的门限值,例如:
一般调整后三个值。
echo“1536,2048,4096,15360,17920,20480″>/sys/module/lowmemorykiller/parameters/minfree

而adj文件存放着oom_adj 内存警戒值( 以4K为单位)
cat到底信息如下:
0 1536 
1 2048 
2 4096 
7 5120 
14 5632 
15 6144

上述参数的含义,当系统的剩余内存为小于6MB时候,警戒级数为0,当系统内存剩余小于8M而大于
6M的时候,警戒级数为1,当内存小于64M大于16MB的时候,警戒级数为12.

 

2.3 在init.rc中对LMK的配置

在init中可以找到如下配置:

# Write value mustbe consistent with the above properties.

   write/sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15

   write/proc/sys/vm/overcommit_memory 1

 write/sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144

进程oom_adj同样可以进行设置,通过write /proc/<PID>/oom_adj,在init.rc中,init进程的pid为1,omm_adj被配置为-16,永远不会被杀死。

   #Set init its forked children's oom_adj.

   write/proc/1/oom_adj -16

dumpsys activity可以dump进程的信息,查看adj值。procrank可以查看进程占用内存大小,当然也可以使用top或者cat mininfo等节点确定。

2.4 OOM Killer

    Linux下还有一种OOM KILLER 的机制,如果上述各种方法(包括LMK)都无法释放出足够的内存空间,那么当为新的进程分配应用程序时将发生 Out-of-Memorykiller异常,OOM_killer 将尽最后杀掉一些进程来释放空间,它会在系统内存耗尽的情况下,启用自己算法有选择性的kill 掉一些进程。

2.5 OOM原理

  当我们使用应用时,需要申请内存,即进行malloc的操作,进行malloc操作如果返回一个非NULL的操作表示申请到了可用的内存。事实上,这个地方是可能存在bug的。Linux有一种内存优化机制,即:允许程序申请比系统可用内存更多的内存,但是Linux并不保证这些内存马上可用,如果凑 巧你申请到的内存中在你需要使用的时候还没有完全释放出来,这个时候就会触发OOM killer了。内核代码为:mm/oom_kill.c,其调用顺序为:
malloc -> _alloc_pages -> out_of_memory() -> select_bad_process() -> badness()
OOM如何选择要kill掉的进程
  分析badness代码,其选择过程如下:
  1)计算该进程以及其子进程所占用的内存;
  2)计算CPU时间和存活时间
  3)做相应的权重调整
  总结起来,就是占用内存越高,得分越高,cpu时间和存活时间越高,得分越低;进程优先级越高,得分越低
  综合上述因素后,会得到一个point的值,得分最高的会被选中,然后被kill掉。

源码位于: oom_kill.c


原创粉丝点击