android的低内存管理器【转】
本文转载自:http://blog.csdn.net/haitaoliang/article/details/22092321
版权声明:本文为博主原创文章,未经博主允许不得转载。
安卓应用不用太在意内存回收,首先Android上的应用是带有独立Java虚拟机的,也就是每开一个应用就会打开一个独立的虚拟机,虚拟机有自己的一套规则回收内存;同时和java的垃圾回收机制类似,android系统有一个规则来主动回收内存。进行内存回收有个阀值,只有低于这个值系统才会按一个列表来关闭用户不需要的东西。
下面介绍android内核的低内存管理,由于上述的这个阈值存在,所以我们会看到android内存使用比例并不高。事实上高剩余内存并不影响速度,相反加快了下次启动应用的速度。这本来就是 android标榜的优势之一,如果人为去关闭进程,没有太大必要,用户体验也不好。比Linux的标准的OOM(Out Of Memory)机制更加灵活,它可以根据需要杀死进程以释放需要的内存 。源代码 位 于 drivers/staging/android/lowmemorykiller.c。
linux使用OOM(Out Of Memory,内存不足)的机制来完成这个任务,该机制会在系统内存不足的情况下,选择一个进程并将其 Kill 掉。Android则使用了一个新的机制——Low Memory Killer 来完成同样的任务。下面首先来看看 Low MemoryKiller 机制的原理以及它是如何选择将被 Kill 的进程的。
1.Low Memory Killer 的原理和机制
Low Memory Killer 在用户空间中指定了一组内存临界值,当其中的某个值与进程描述中的oom_adj值在同一范围时,该进程将被Kill掉。在“/sys/module/lowmemorykiller/parameters/adj”中指定oom_adj的最小值,在“/sys/module/lowmemorykiller/parameters/minfree” 中指定空闲页面的数量,所有的值都用一个逗号将其隔开且以升序排列。比如:把“0,8”写入到/sys/module/lowmemorykiller/parameters/adj中,把“1024,4096”写入到/sys/module/lowmemory-killer/parameters/minfree 中,就表示当一个进程的空闲存储空间下降到 4096 个页面时,oom_adj 值为 8 或者更大的进程会被 Kill 掉。同理,当一个进程发现空闲存储空间下降到 1024 个页面时, oom_adj 值为 0 或者更大的进程会被 Kill 掉。在 lowmemorykiller.c 中就指定了这样的默认值,如下所示:
static int lowmem_adj[6] = {
0,
1,
6,
12,
};
static int lowmem_adj_size = 4;
static size_t lowmem_minfree[6] = {
3*512, // 单位页,6MB
2*1024, // 8MB
4*1024, // 16MB
16*1024, // 64MB
};
static int lowmem_minfree_size = 4;
这就说明,当一个进程的空闲空间下降到 3*512 个页面时,oom_adj 值为 0 或者更大的进 程会被 Kill 掉;当一个进程的空闲空间下降到 2*1024 个页面时,oom_adj 值为 10 或者更大的进程会被 Kill 掉,依此类推。
进程的oom_adj值是由上层决定的,Android将进程分为6个等级,分别对应不同的lowmem_adj,它们按优先级顺序由高到低依次是:
1前台进程(foreground)
目前正在屏幕上显示的进程和一些系统进程。举例来说,Dialer,Storage,Google Search等系统进程就是前台进程;再举例来说,当你运行一个程序,如浏览器,当浏览器界面在前台显示时,浏览器属于前台进程(foreground),但一旦你按home回到主界面,浏览器就变成了后台程序(background)。我们最不希望终止的进程就是前台进程。
2可见进程(visible)
可见进程是一些不再前台,但用户依然可见的进程,举个例来说:widget、输入法等,都属于visible。这部分进程虽然不在前台,但与我们的使用也密切相关,我们也不希望它们被终止(还比如时钟、天气,新闻等widget用户肯定不希望被终止,那它们将无法同步,你也不希望输入法被终止,否则你每次输入时都需要重新启动输入法)。
3桌面进程(home app)
即launcher(桌面启动器),保证在多任务切换之后,可以快速返回到home界面而不需重新加载launcher。
4次要服务(secondary server)
目前正在运行的一些服务(主要服务,如拨号等,是不可能被进程管理终止的,故这里只谈次要服务),举例来说:谷歌企业套件,Gmail内部存储,联系人内部存储等。这部分服务虽然属于次要服务,但很一些系统功能依然息息相关,我们时常需要用到它们,所以也不太希望他们被终止。
5后台进程(hidden)
即是后台进程(background),就是我们通常意义上理解的启动后被切换到后台的进程,如浏览器,阅读器等。当程序显示在屏幕上时,他所运行的进程即为前台进程(foreground),一旦我们按home返回主界面(注意是按home,不是按back),程序就驻留在后台,成为后台进程(background)。
后台进程的管理策略有多种:有较为积极的方式,一旦程序到达后台立即终止,这种方式会提高程序的运行速度,但无法加速程序的再次启动;也有较消极的方式,尽可能多的保留后台程序,虽然可能会影响到单个程序的运行速度,但在再次启动已启动的程序时,速度会有所提升。这里就需要用户根据自己的使用习惯找到一个平衡点。
6内容供应节点(content provider)
没有程序实体,仅提供内容供别的程序去用的,比如日历供应节点,邮件供应节点等。在终止进程时,这类程序应该优先杀死。
init.rc(android上层代码)中对adj与minfree默认值进行设置,在内核里分别对应lowmem_adj和lowmem_minfree
# Define the oom_adj values for the classes of processesthat can be
# killed by the kernel. These are used inActivityManagerService.
setprop ro.FOREGROUND_APP_ADJ 0
setprop ro.VISIBLE_APP_ADJ 1
setprop ro.SECONDARY_SERVER_ADJ 2
setprop ro.HIDDEN_APP_MIN_ADJ 7
setprop ro.CONTENT_PROVIDER_ADJ 14
setprop ro.EMPTY_APP_ADJ 15
# Define the memory thresholds at which the above processclasses will
# be killed. These numbers are in pages (4k).
setprop ro.FOREGROUND_APP_MEM 1536
setprop ro.VISIBLE_APP_MEM 2048
setprop ro.SECONDARY_SERVER_MEM 4096
setprop ro.HIDDEN_APP_MEM 5120
setprop ro.CONTENT_PROVIDER_MEM 5632
setprop ro.EMPTY_APP_MEM 6144
进程描述符中的 signal_struct->oom_adj 表示当内存短缺时进程被选择并 Kill 的优先级,取值范围是−17~15。如果是−17,则表示不会被选中,值越大越可能被选中。当某个进程被选中 后,内核会发送 SIGKILL 信号将其 Kill 掉,进程oom优先级别可以通过写/proc/<PID>/oom_adj进行设置。
2.Low Memory Killer 的具体实现
在了解了 Low Memory Killer 的原理之后,我们再来看如何实现这个驱动。Low MemoryKiller 驱动的实现位于drivers/misc/lowmemorykiller.c。
该驱动的实现比较简单,在 初 始 化 函 数 lowmem_init 中 通 过 register_shrinker 注 册 了 一 个shrinker为lowmem_shrinker:
static int __init lowmem_init(void)
{
register_shrinker(&lowmem_shrinker);
return 0;
}
static void __exit lowmem_exit(void)
{
unregister_shrinker(&lowmem_shrinker);
}
module_init(lowmem_init);
module_exit(lowmem_exit);
退出时又调用了函数lowmem_exit,通过 unregister_shrinker来卸载被注册的lowmem_shrinker。其中lowmem_shrinker的定义如下:
static struct shrinker lowmem_shrinker = {
.shrink = lowmem_shrink,
.seeks = DEFAULT_SEEKS * 16
};
lowmem_shrink 是这个驱动的核心实现,当内存不足时就会调用 lowmem_shrink 方法来 Kill掉某些进程shrink_slab -> do_shrinker_shrink->lowmem_shrink。kswapd线程将会遍历一张shrinker链表,下面来分析其具体实现,实现代码如下:
对每个满足sig->oom_adj大于min_adj的进程,找到占用内存最大的进程存放在selected中:
static int lowmem_shrink(struct shrinker *s, structshrink_control *sc)
{
struct task_struct*p;
struct task_struct*selected = NULL;
int rem = 0;
int tasksize;
int i;
int min_adj =OOM_ADJUST_MAX + 1;
intselected_tasksize = 0;
int selected_oom_adj;
int array_size =ARRAY_SIZE(lowmem_adj);
int other_free =global_page_state(NR_FREE_PAGES);
int other_file =global_page_state(NR_FILE_PAGES) -
global_page_state(NR_SHMEM);
/*
* If we already have a death outstanding, then
* bail out right away; indicating to vmscan
* that we have nothing further to offer on
* this pass.
*
*/
if(lowmem_deathpending &&
time_before_eq(jiffies,lowmem_deathpending_timeout))
return 0;
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_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_adj);
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);
if(sc->nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) {
lowmem_print(5,"lowmem_shrink %lu, %x, return %d\n",
sc->nr_to_scan, sc->gfp_mask, rem);
return rem;
}
selected_oom_adj =min_adj;
read_lock(&tasklist_lock);
for_each_process(p){
structmm_struct *mm;
structsignal_struct *sig;
int oom_adj;
task_lock(p);
mm = p->mm;
sig =p->signal;
if (!mm ||!sig) {
task_unlock(p);
continue;
}
oom_adj =sig->oom_adj;
if (oom_adj< min_adj) {
task_unlock(p);
continue;
}
tasksize = get_mm_rss(mm);
task_unlock(p);
if (tasksize<= 0)
continue;
if (selected) {
if (oom_adj< selected_oom_adj)
continue;
if (oom_adj== selected_oom_adj &&
tasksize <= selected_tasksize)
continue;
}
selected = p;
selected_tasksize= tasksize;
selected_oom_adj= oom_adj;
lowmem_print(2,"select %d (%s), adj %d, size %d, to kill\n",
p->pid, p->comm, oom_adj, tasksize);
}
if (selected) {
lowmem_print(1,"send sigkill to %d (%s), adj %d, size %d\n",
selected->pid, selected->comm,
selected_oom_adj, selected_tasksize);
lowmem_deathpending= selected;
lowmem_deathpending_timeout= jiffies + HZ;
force_sig(SIGKILL,selected);
rem -=selected_tasksize;
}
lowmem_print(4,"lowmem_shrink %lu, %x, return %d\n",
sc->nr_to_scan, sc->gfp_mask, rem);
read_unlock(&tasklist_lock);
return rem;
}
lowmem_shrink函数首先确定我们所定义的lowmem_adj和 lowmem_minfree 数组的大小(元素个数)是否一致,如果不一致则以最小的为基准。因为我们需要通过比较lowmem_minfree中的空闲储存空间的值,以确定最小min_adj值(当满足其条件时,通过其数组索引来寻找lowmem_adj中对应元素的值);然后使用循环对每一个进程块进行判断,通过min_adj来寻找满足条件的具体进程,并且找到满足条件的tasksize最大的进程;最后如果存在这样的进程,通过“force_sig(SIGKILL, selected)”发送一条SIGKILL信号到内核, Kill 掉被选中的 “selected”进程。
最后简单描述一下标准 Linux 的 OOM Killer 机制。在分 配内存时,即mm/page_alloc.c中内存紧张会调用__alloc_pages_may_oom。oom_kill.c 最主要的一个函数是 out_of_memory,它选择一个bad进程Kill, Kill的方法同样是通过发送SIGKILL信号。在 out_of_memory 中通过调用select_bad_process 来 选择一个进程 Kill,选择的依据在oom_badness函数中实现,2.6.36后内核引入多个标准来给每个进程评分,评分最高的被选中并 Kill。一般而言,占用内存越多,/proc/pid/oom_score_adj越大,非root进程等因素越有可能被选中,可见比android要简单。
android的低内存管理器【转】的更多相关文章
- Android应用的内存管理
管理应用的内存可以分为两个部分内容: 1. 首先需要理解:How Android Manages App Processes and Memory Allocation? 2. 其次需要考虑:我们设计 ...
- Android中的内存管理机制以及正确的使用方式
概述 从操作系统的角度来说,内存就是一块数据存储区域,属于可被操作系统调度的资源.现代多任务(进程)的操作系统中,内存管理尤为重要,操作系统需要为每一个进程合理的分配内存资源,所以可以从两方面来理解操 ...
- PHP V5.2 中的新增功能,第 1 部分: 使用新的内存管理器
PHP V5.2:开始 2006 年 11 月发布了 PHP V5.2,它包括许多新增功能和错误修正.它废止了 5.1 版并被推荐给所有 PHP V5 用户进行升级.我最喜欢的实验室环境 —— Win ...
- TaskTracker节点上的内存管理器
Hadoop平台的最大优势就是充分地利用了廉价的PC机,这也就使得集群中的工作节点存在一个重要的问题——节点所在的PC机内存资源有限(这里所说的工作节点指的是TaskTracker节点),执行任务时常 ...
- STL内存管理器的分配策略
STL提供了很多泛型容器,如vector,list和map.程序员在使用这些容器时只需关心何时往容器内塞对象,而不用关心如何管理内存,需要用多少内存,这些STL容器极大地方便了C++程序的编写.例如可 ...
- C/C++内存管理器
C标准库提供了malloc,free,calloc,realloc,C++标准库还提供了new, new[], delete, delete[].这些用来管理内存,看起来够用了,为啥还要自己写一个内存 ...
- BBS项目详解(forms快速创建登陆页面,登陆验证、通过阅读器进行头像上传的预览、内存管理器)
BBS项目涉及的知识点 django中知识点 钩子函数(局部钩子和全局钩子) 1.局部钩子就是用来做合法性校验,比如用户名有没有被使用等 2.全局的就是用来做对比校验,比如两次输入的密码是否一致 3. ...
- DLL何时需共享内存管理器
Delphi创建DLL时,IDE自动生成的文档中写得很清楚,当在DLL中以动态数组或String做为参数或返回值时(即RTL自动维护的数据类型),请在每个工程文件的第一个单元加上ShareMem.这样 ...
- spark内存管理器--MemoryManager源码解析
MemoryManager内存管理器 内存管理器可以说是spark内核中最重要的基础模块之一,shuffle时的排序,rdd缓存,展开内存,广播变量,Task运行结果的存储等等,凡是需要使用内存的地方 ...
随机推荐
- HTTPS的中那些加密算法
密码学在计算机科学中使用非常广泛,HTTPS就是建立在密码学基础之上的一种安全的通信协议.HTTPS早在1994年由网景公司首次提出,而如今在众多互联网厂商的推广之下HTTPS已经被广泛使用在各种大小 ...
- 使用composer 实现自动加载
准备工作:提前安装好composer 1.创建项目目录OOP 2.OOP目录下新建composer.json文件,composer.json是一个空json文件,代码如下: { } 3.打开控制台,进 ...
- Hibernate_01_初体验
hibernate开发的基本步骤: 编写配置文档hibernate.cfg.xml: 编写实体类: 生成对应实体类的映射文件并添加到配置文档中: 调用hibernate API进行测试. Hibern ...
- SQL Server之存储过程
存储过程的概念 存储过程Procedure是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中,用户通过指定存储过程的名称并给出参数来执行. 存储过程中可以包含逻辑控制语句和数据操纵语句,它 ...
- Oracle数据库的导入和导出
Oracle数据库的导入和导出,是一项重要的的技术活,不但解决了数据库的导入导出,更方便快捷的获得数据. 使用imp和exp导入导出数据 使用exp导出数据 存放目录为\ORACLE_HOME\BIN ...
- emlog通过pjax实现无刷新加载网页--完美解决cnzz统计和javascript失效问题
想要更详细了解pjax,需要查看官网 或者看本站文章:jQuery.pjax.js:使用AJAX和pushState无刷新加载网页(官网教程中文翻译) 效果看本站,音乐无刷新播放,代码高亮和复制js加 ...
- (转)Bootstrap 之 Metronic 模板的学习之路 - (3)源码分析之 body 部分
https://segmentfault.com/a/1190000006697252 body 的组成结构 body 部分包含了 HEADER.CONTAINER.FOOTER,其中 CONTAIN ...
- HDU_1850_nim游戏
Being a Good Boy in Spring Festival Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32 ...
- 论 fmap、fmap fmap、与 fmap fmap fmap
https://blog.csdn.net/sinat_25226993/article/details/44415803
- Asp.Net Core 中利用QuartzHostedService 实现 Quartz 注入依赖 (DI)
QuartzHostedService 是一个用来在Asp.Net Core 中实现 Quartz 的任务注入依赖的nuget 包: 基本示例如下: using System; using Syst ...