Android 的lowmemorykiller机制

转自:http://blog.csdn.net/u012440406/article/details/51960387

最近在处理一些lowmemorykiller相关的问题,于是对lowmemorykiller机制作了一个简单的了解。在这里总结一下。

首先,是lowmemorykiller的一些背景知识。

众所周知,Andorid的实质是一个linux的操作系统。所以和其他操作系统一样,每个程序,每个进程运行,都需要一定内存空间进行支撑。而进程的内存空间只是虚拟内存,程序运行需要的是实实在在的内存(物理内存,即RAM)。所以在必要的时候,操作系统会将程序运行中申请的内存映射到RAM中。RAM作为进程运作不可缺的资源,对系统稳定性有着决定性影响。所以我们必须对内存相关有一个简单直观的认知。进程空间1和RAM之间的关系大致如图:

内存相关的介绍,在这里我只是做一个简单介绍。假如想深入了解请自行了解操作系统相关知识。

简单对内存有一个了解之后,我们来简单介绍一下OOM。

OOM全称Out Of Memory,是Linux当中,内存保护机制的一种。该机制会监控那些占用内存过大,尤其是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核将该进程杀掉。

当Kernel遇到OOM的时候,可以有2种选择:

1)  产生kernelpanic(死机)

2)  启动OOM killer,选择一个或多个“合适”的进程,干掉那些选择中的进程,从而释放内存。

在OOM机制当中,有几个参数是必须了解的,这几个参数分别是oom_adj,oom_score_adj,oom_score。每个进程都会有这样的3个参数,他们位于/proc/XXX/目录下(XXX为进程的ID)。在Linux中,系统就是通过算分去杀死进程的。至于分数的值,就是这3个参数的值。

简单来说,系统是这样进行算分的:算分主要分2部分,一部分是系统打分,主要根据该进程的内存使用情况(oom_score),另一部分是用户大份额,就是oom_score_adj。每个进程的实际得分是综合这2个参数的值的。而oom_adj只是一个旧的接口参数,在普通的linux系统中和oom_score_adj是差不多的(但是在android中是很有用的)。

OOM是Android的LowMemoryKiller的基础。了解完OOM之后,我们终于可以引入LowMemoryKiller了。

在Android中,及时用户退出当前应用程序后,应用程序还是会存在于系统当中,这是为了方便程序的再次启动。但是这样的话,随着打开的程序的数量的增加,系统的内存就会不足,从而需要杀掉一些进程来释放内存空间。至于是否需要杀进程以及杀什么进程,这个就是由Android的内部机制LowMemoryKiller机制来进行的。

Andorid的Low Memory Killer是在标准的linux lernel的OOM基础上修改而来的一种内存管理机制。当系统内存不足时,杀死不必要的进程释放其内存。不必要的进程的选择根据有2个:oom_adj和占用的内存的大小。oom_adj代表进程的优先级,数值越高,优先级月低,越容易被杀死;对应每个oom_adj都可以有一个空闲进程的阀值。Android Kernel每隔一段时间会检测当前空闲内存是否低于某个阀值。假如是,则杀死oom_adj最大的不必要的进程,如果有多个,就根据oom_score_adj去杀死进程,,直到内存恢复低于阀值的状态。

LowMemoryKiller的值的设定,主要保存在2个文件之中,分别是/sys/module/lowmemorykiller/parameters/adj与/sys/module/lowmemorykiller/parameters/minfree。adj保存着当前系统杀进程的等级,minfree则是保存着对应的阀值。他们的对应关系如下:

举个例子说明一下上表,当当前系统内存少于55296×4K(即216MB)时,Android就会找出当前oom_adj≥9的进程,根据进程的等级,先把oom_adj数值最大的进程给杀掉,释放他的内存,当他们的oom_adj相等时,就对比他们的oom_score_adj,然后oom_score_adj越大,也越容易杀掉。

在这里,也许有人会问,为什么采用LowMemoryKiller而不用OOM呢?我们来对比一下两者,就可以得出答案了。

使用LowMemoryKiller可以使系统内存较低时,调出进程管理器结束不必要的人进程释放空间。在安卓中,如果等到真正的OOM时,也许进程管理器就已经没法启动了。

上面提到的oom_adj,其实在Android中并不只有6个,在Android 6.0中,一个设置了16个adj,adj的具体设置与描述如下:

对LowMemoryKiller相关的知识简介就到这里。下面我们就主要介绍一下lowMemorykiller的运行原理。

上面其实已经说过,LowMemoryKiller是对多个内存阀值的控制来选择杀进程的。但是,这些阀值是怎样联系在一起的呢?下面就我的理解,简单说一下其运行的原理。

首先,LowMemoryKiller是随着系统的启动而启动的。当前主要的LowMemoryKiller的代码主要在\system\core\lmkd的目录下,之前的代码\kernel\drivers\staging\android\lowmemorykiller.c已经不再使用。

LowMemoryKiller在系统启动的时候就已经由init进程一并启动了。LowMemoryKiller启动就是,就会不断监测系统的运行情况和内存情况,当内存少于minfree限定的阀值的时候,lowMemoryKiller遍历当前进程的oom_score_adj,把大于对应阀值的进程进行kill操作。例如,在当前设置中,当系统内存少于315M时,系统就会自动把进程中oom_score_adj的值少于1000的杀掉,当系统内存少于216时,系统就会自动把进程中oom_score_adj的值少于529的杀掉,如此类推。

至于oom_adj和oom_score_adj是由谁去控制并写入的呢?

在系统当中,oom_adj和oom_score_adj是由ActivityManagerService去控制的,上层应用的启动都离不开AcitivityManagerService的调用与分配资源。有关oom_adj与oom_score_adj会在以后分析ActivityManagerService的时候加入相对详细的论述。在这里就不详细说明。

有关Minfree的值的写入,其实可以找到很多个地方,但是在最开始(还在用lowmemorykiller.c)的时候,是可以在lowmemorykiller.c中设置的。但是现在已经不用lowmemorykiller.c了,所以相对设置的地方也不一样了。经查找验证,Minfree的阀值控制,是由ActivictyManagerService和lowmemorykiller一并控制写入的。

在ActivictyManagerService中,会调用ProcessList的applyDisplaySize()方法,从而调用updateOomLevels()的方法,开始算出相关的阀值.代码如下:

[java] view plain copy

  1. private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
  2. ……
  3. final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
  4. for (int i=0; i<mOomAdj.length; i++) {
  5. int low = mOomMinFreeLow[i];
  6. int high = mOomMinFreeHigh[i];
  7. if (is64bit) {
  8. Slog.i("XXXXXX", "choosing minFree values for 64 Bit");
  9. // 64位系统中,第4,5等级会略大
  10. if (i == 4) high = (high*3)/2;
  11. else if (i == 5) high = (high*7)/4;
  12. } else {
  13. Slog.i("XXXXXX", "choosing minFree values for 32 Bit");
  14. low = mOomMinFreeLow32Bit[i];
  15. high = mOomMinFreeHigh32Bit[i];
  16. }
  17. // mOomMinFree的值,是这样算出来的。但是scale的值是1,所以实际上他是high的值
  18. mOomMinFree[i] = (int)(low + ((high-low)*scale));
  19. }
  20. //当minfree_abs>=0或minfree_adj!=0时,mOomMinFree的值还要再算一次
  21. if (minfree_abs >= 0) {
  22. for (int i=0; i<mOomAdj.length; i++) {
  23. mOomMinFree[i] = (int)((float)minfree_abs * mOomMinFree[i]
  24. / mOomMinFree[mOomAdj.length - 1]);
  25. }
  26. }
  27. if (minfree_adj != 0) {
  28. for (int i=0; i<mOomAdj.length; i++) {
  29. mOomMinFree[i] += (int)((float)minfree_adj * mOomMinFree[i]
  30. / mOomMinFree[mOomAdj.length - 1]);
  31. if (mOomMinFree[i] < 0) {
  32. mOomMinFree[i] = 0;
  33. }            }
  34. }
  35. ……
  36. if (write) {
  37. ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
  38. buf.putInt(LMK_TARGET);
  39. for (int i=0; i<mOomAdj.length; i++) {
  40. if(i==5){
  41. mOomMinFree[i] = 307200;
  42. }
  43. buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
  44. buf.putInt(mOomAdj[i]);
  45. }
  46. //调用writeLMkd,将内容传给lmkd.c,写入到minfree文件中。
  47. writeLmkd(buf);
  48. SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
  49. }
  50. }

上面可以看出,LowMemoryKiller的阀值是通过多个判断然后算出来的。不过,在正常情况下,除了4,5等级之外,LowMemoryKiller的0-3的阀值其实就是mOomMinFreeHigh32Bit[]里面的值。我们以后假如需要对LowMemoryKiller的阀值进行定制,只需要针对这个方法去修改即可。

另外,刚刚说到,Minfree的阀值控制,是由ActivictyManagerService和LowMemoryKiller一并控制写入的。ActivictyManagerService把值确定了,通过socket机制,和LowMemoryKiller进行通讯,然后LowMemoryKiller就会把值写入minfree中。

在LowMemoryKiller的代码(lmkd.c)中,接受写入minfree的关键代码如下:

[cpp] view plain copy

  1. static void ctrl_connect_handler(uint32_t events __unused) {
  2. //……
  3. alen = sizeof(addr);
  4. //接收到ActivictyManagerService传过来的阀值
  5. ctrl_dfd = accept(ctrl_lfd, &addr, &alen);
  6. if (ctrl_dfd < 0) {
  7. ALOGE("lmkd control socket accept failed; errno=%d", errno);
  8. return;
  9. }
  10. ALOGI("ActivityManager connected");
  11. maxevents++;
  12. epev.events = EPOLLIN;
  13. //将阀值保持进minfree
  14. epev.data.ptr = (void *)ctrl_data_handler;
  15. //……

ctrl_connect_handler()在接受到数据之后,经过多步的处理最后会调用writefilestring()将值进行写入。

[cpp] view plain copy

  1. static void writefilestring(char *path, char *s) {
  2. int fd = open(path, O_WRONLY);
  3. int len = strlen(s);
  4. int ret;
  5. if (fd < 0) {
  6. ALOGE("Error opening %s; errno=%d", path, errno);
  7. return;
  8. }
  9. ret = write(fd, s, len);
  10. if (ret < 0) {
  11. ALOGE("Error writing %s; errno=%d", path, errno);
  12. } else if (ret < len) {
  13. ALOGE("Short write on %s; length=%d", path, ret);
  14. }
  15. close(fd);
  16. }

Minfree的值是每次开机都会进行写入的,所以假如我们只是单纯地在手机上,直接修改adj和minfree的值,重启之后是不会生效的。

那假如我们想定制某个应用的adj呢?假如只是定制单个应用的adj,其实我们可以在ActivityManagerService中的computeOomAdjLocked方法中进行定制。系统会调用这个方法不停更新正在运行的进程的adj。例如,假如我们想修改launcher默认的adj(默认launcher在后台运行时的adj为6),我们可以在computeOomAdjLocked中的:

[java] view plain copy

  1. if (app == mHomeProcess) {
  2. if (adj > ProcessList.HOME_APP_ADJ) {
  3. // This process is hosting what we currently consider to be the
  4. // home app, so we don't want to let it go into the background.
  5. adj = ProcessList.HOME_APP_ADJ;
  6. schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
  7. app.cached = false;
  8. app.adjType = "home";
  9. }
  10. if (procState > ActivityManager.PROCESS_STATE_HOME) {
  11. procState = ActivityManager.PROCESS_STATE_HOME;
  12. }
  13. }

进行修改即可。

我们可以通过app这个对象去获得更新的进程的包名以便定制。

有关LowMemoryKiller的分析就到这里。

Android 系统(222)---Android 的lowmemorykiller机制相关推荐

  1. Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析

    原文地址: http://blog.csdn.net/luoshengyang/article/details/6629298 在前面一篇文章浅谈Android系统进程间通信(IPC)机制Binder ...

  2. 浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路

    原文地址: http://blog.csdn.net/luoshengyang/article/details/6627260 在前面一篇文章浅谈Service Manager成为Android进程间 ...

  3. Android系统进程间通信(IPC)机制Binder中的Client获得Server远程接口过程源代码分析(2)...

    注意,这里的参数reply = 0,表示这是一个BC_TRANSACTION命令.         前面我们提到,传给驱动程序的handle值为0,即这里的tr->target.handle = ...

  4. Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析(1)

    在前面一篇文章浅谈Android系统进程间通信(IPC)机制Binder中的Server和Client获得Service Manager接口之路中,介绍了在Android系统中Binder进程间通信机 ...

  5. Android系统中的Binder通信机制分析(7)- Java 层的 Binder 机制

    声明 其实对于Android系统Binder通信的机制早就有分析的想法,记得2019年6.7月份Mr.Deng离职期间约定一起对其进行研究的,但因为我个人问题没能实施这个计划,留下些许遗憾- 文中参考 ...

  6. Android系统架构-[Android取经之路]

    摘要:本节主要来讲解Android的系统架构 阅读本文大约需要花费10分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢! 欢 ...

  7. android 服务端技术,移动应用服务器端开发(基于JSP技术)-2017 Android系统构架 Android系统构架.docx...

    Android系统构架 PAGE 1 目 录 TOC \o "1-3" \h \z \u 一.Android系统构架 1 二.Linux内核层 2 三.系统运行库层 3 (一)系统 ...

  8. 【android系统】android系统升级流程分析(二)---update升级包分析

    接下来我们将通过几篇文章来分析update.zip包在具体Android系统升级的过程,来理解Android系统中Recovery模式服务的工作原理.今天让我先来分析下升级包update.zip. 一 ...

  9. 【android系统】android系统升级流程分析(一)---recovery模式中进行update包升级流程分析

    今天我们直接来看下android中具体的升级过程是如何的. 升级流程概述 升级的流程图: 升级流程分析 第一步:升级包获取 升级获取可以通过远程下载,也可直接拷贝到指定目录即可. 第二步:准备升级 然 ...

  10. android log抓取方法,Android系统之Android抓取各种log的方法

    Android系统之Android抓取各种log的方法 2018年11月25日 | 萬仟网移动技术 | 我要评论 android之android抓取各种log的方法 1.logcat (四类log b ...

最新文章

  1. 左线性文法和右线性文法_线性代数期末考试复习资料
  2. java中哈夫曼编码所用的函数_数据结构(java语言描述)哈夫曼编码
  3. 【2021】一位清华大佬的互联网秋招算法岗总结
  4. python二分法查找
  5. java和equals区别_JAVA中==与equals的区别
  6. [转]C#多线程学习(三) 生产者和消费者
  7. 20170429,上市公司2016年报全出炉(附最新排行榜)
  8. MySQL查询出来的不重复
  9. 字符串经典题之正则匹配字符串
  10. Linux命令的学习(目前需要的,紧急)
  11. Netty工作笔记0077---handler链调用机制实例4
  12. 【学习 OpenCV】—— 将一个3通道的像素点转换到新的彩色空间
  13. Python文件输入输出
  14. Django+xadmin的安装与配置
  15. 如何让textarea不可拖拽变化大小
  16. 数据挖掘05-偏相关分析【原理、案例、python实现】
  17. 火车票电子客票系统已全面上线,如何识别多种身份有效证件?
  18. 【观察】嘉诚信息:为智慧检务按下“加速键”
  19. 爬虫pyquery查找节点
  20. Newton tangent method

热门文章

  1. Nginx基本数据结构之ngx_pool_t
  2. Linux的capability深入分析(1)
  3. [计算机网络] - HTTP、HTTPS
  4. html压缩原理,webpack--前端性能优化与Gzip原理
  5. 从memcpy到memmove,内存函数拷贝与内存重叠问题(重点内容)
  6. c语言编程 排序,C语言编程-9_3 排序
  7. 高一计算机应用选择题,职业中学 高一《计算机应用基础》期末考试题
  8. ML、DL、CNN学习记录6
  9. java并发:初探消费者和生产者模式
  10. go语言基础之不同作用域同名变量