android内存管理(一)
来源:互联网 发布:nginx content by lua 编辑:程序博客网 时间:2024/06/08 01:12
虚拟内存
如果在程序被挂起或被换出前仅仅使用了一部分进程快,那么为该进程给内存中装入太多的块显然会带来巨大的浪费。而虚拟内存借助磁盘和内存交换,仅仅装入这小部分块来更好地使用内存,然后,如果程序转移到或访问到不在内存中的某个快中的指令或数据时,就会引发一个中断,告诉操作系统读取需要的块。
我们知道进程中的所有内存访问都是逻辑地址,这些逻辑地址在运行时动态的被转换成物理地址,而这意味着一个进程可以被换入或换出内存,使得进程可以在执行过程中的不同时刻占据内存中的不同区域。还有一个进程可以划分成许多块,在执行过程中,这些块不需要连续的位于内存中。这两点也是虚拟内存突破所在。
接下来看虚拟内存重要的一些概念和组成
- 页号:每个进程内存都可以分割成好多块,每一块有对应的页号标记。
- 页框号:正真物理内存也相当于被分成N多块,每一块对应其中一个页框号
- 页表项:它通常由页号是否存在内存标记,控制位,页框号等组成,是一个检索页号对应的页框号,每一个页对应一个页表项。
- 页表:有多个页表项组成,当启动进程时,该页表会加载到内存,并通过一些策略管理页表项。
- 虚拟地址(逻辑地址):页号,偏移量
- 物理地址:页框号,偏移量,就是内存实际地址
- 转换检测缓冲区(TLB):它是位于CPU和内存之间的一个高速缓存存储器,用来存储访问过的页表项,避免了每个虚拟内存访问引起了两次物理内存访问(一次页表项,另一次读取需要的数据)。
- 辅存:就是物理磁盘之类存储外介
其虚拟内存分页地址转换过程如图:
其转换检测缓冲区的使用:
虚拟内存整个操作流程:
内存的分配层
Native层:本地层的程序基本上是由c/c++编写的,与开发人员直接相关的内存函数包括malloc/free,new/delete等,这部分不受Java Object Head的大小限制,但它会受到系统的限制,这块动态分配的内存需要人工管理,容易出现内存致命Bug,因此谷歌应用了智能指针来尽量避免这块,如果开发人员过多使用Native层去分配内存的话,可能会引发系统采取激进的方式杀掉某些进程。
Java层(Java Object Head):它的最大值就是指我们Android应用程序能够使用的最大内存,它通常是通过new,或者加载图像(External Memory也叫Bitmap Memory)分配内存,可以通过-Xms和-Xmx选项来指定Java Object Heap的最小值和最大值,但相对于Native层,其分配的内存不需要人工去管理释放。
进程间通信——mmap
一般来说,如果进程间通讯不通过内存映射共享的话,每次的进程通讯会涉及到四次的复制操作,并且这四次进程复制操作是发生在内核和进程两者之间,而不是发生内核或进程两者之间,这中复制操作开销相对于后两者复制操作来说较大,其进程通讯过程如图:
从图中来看,四次过程,分贝是两次read(),write()方法,首先server从文件读取数据到进程空间中,再从进程写到内核通讯通道中,然后client再从该内核通道中读取数据到它的进程空间中,最后才写到输出文件中。
其mmap可以将某个设备或者文件映射到应用进程的内存空间中,这样访问这块内存就相当于对设备/文件进行读写,而不需要再通过两次的read()和两次的write()了,而仅仅需要一次read()和一次write(),因此Android经常通过mmap用于进程间通信,即通过映射同一块物理内存来共享内存,其整个过程如图:
关于mmap性能及详细细节,请参考以下这篇博客,这篇博客有提到过:
http://blog.csdn.net/xuguoli_beyondboy/article/details/45085703
Low Memory Killer
当运行的程序超过一定数量,或者涉及复杂的计算时,很可能出现内存不足,进而导致系统卡顿的现象,在Android中,用户如果启动的应用程序(如Activity Finish()),该进程不是马上被清理,而是驻留在内存中,这样加快再次启动的速度,但增加了内存开销。
android在管理内存不足时,主要通过lowmem_adj和lowmem_minfree去判断,其中lowmen_adj和lowmem_minfreed一一对应,其中adj的取值范围是-17~15,数字越小表示进程级别越高(通常只有0~15被使用)。
源码分析:
Lowmemorykiller.c
//默认定义了4个static int lowmem_adj[6] = { 0, 1, 6, 12,};//这是默认一页为4kBstatic int lowmem_adj_size = 4;//默认定义了4个//lowmem_minfree[i]*lowmem_adj_size=nMBstatic size_t lowmem_minfree[6] = { 3 * 512, /* 6MB */ 2 * 1024, /* 8MB */ 4 * 1024, /* 16MB */ 16 * 1024, /* 64MB */};
第一个数组lowmen_adj最多有6个元素(默认只定义了4个),它adj值对应是否杀掉该层级进程的内存容量临界值,第二个数组对应”层级”的描述,如: lowmen_minfree的第一个元素时3*512,即3*512*lowmen_adj_size=6MB,其对应的adj的值为0,如果Android系统可用内存小于6MB时,adj值为0以下的那些进程就会被清理。
当我们可以下面两个文件去修改,如: /sys/module/lowmemorykiller/parameters/adj 0,8
/sys/module/lowmemorykiller/parameters/minfree 10244096
当系统的内存剩余空间于1024*4~4096*4之间,oom_adj大于或等于8的进程将会被杀掉。
源码分析:
static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask); //当内存较低且需要释放时,会初始化shrinkerstatic struct shrinker lowmem_shrinker = { .shrink = lowmem_shrink, .seeks = DEFAULT_SEEKS * 16};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);static int lowmem_shrink(int nr_to_scan, gfp_t gfp_mask){ struct task_struct *p; struct task_struct *selected = NULL; int rem = 0; int tasksize; int i; int min_adj = OOM_ADJUST_MAX + 1; int selected_tasksize = 0; int selected_oom_adj; //获取lowmen_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); // 获取较小的那个长度的值 if (lowmem_adj_size < array_size) array_size = lowmem_adj_size; if (lowmem_minfree_size < array_size) array_size = lowmem_minfree_size; //查找小于内存临界值的最小min_adj 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 (nr_to_scan > 0) lowmem_print(3, "lowmem_shrink %d, %x, ofree %d %d, ma %d\n", nr_to_scan, 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 (nr_to_scan <= 0 || min_adj == OOM_ADJUST_MAX + 1) { lowmem_print(5, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem); return rem; } selected_oom_adj = min_adj; //获取锁 read_lock(&tasklist_lock); //寻找被杀死进程的处理 for_each_process(p) { struct mm_struct *mm; int oom_adj; task_lock(p); mm = p->mm; if (!mm) { task_unlock(p); continue; } oom_adj = mm->oom_adj; //如果oom_adj小于min_adj就不杀死 if (oom_adj < min_adj) { task_unlock(p); continue; } //该进程的内存占用大小 tasksize = get_mm_rss(mm); task_unlock(p); //内存占用大小不超过0,该进程不被杀死 if (tasksize <= 0) continue; //如果当前的进程比前一个刚被杀死的进程的oom_adj或分配内存大小要小,则该进程不被杀死 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; //输出进程id标识,进程描述,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); force_sig(SIGKILL, selected); rem -= selected_tasksize; } lowmem_print(4, "lowmem_shrink %d, %x, return %d\n", nr_to_scan, gfp_mask, rem); //释放锁 read_unlock(&tasklist_lock); return rem;}
进程可以通过两种方法来改变自己的adj
修改/proc//oom_adj 的值,如在init.rc: on early-init
# Set init and its forked children's oom_adj.
write /proc/1/oom_adj -16
那么这样保证PID为1的进程不会被杀死。
在AndroidManifest.xml文件中给application标签添加”android:persistent=true”属性来确保该进程不被杀死。
进程和oom_adj之间关系
进程可以规划为几块:前台进程,可见进程,服务进程,后台进程,空进程。
它们所属的adj值如图所示:
参考资料:
http://blog.csdn.net/luoshengyang/article/details/8852432
http://www.programering.com/a/MjNzADMwATE.html
http://www.kohala.com/start/unpv22e/unpv22e.chap12.pdf
- android内存管理(一)
- android内存管理(一)
- Android 内存浅析【一】【管理、机制、分析】
- 内存管理(一)
- Linux内存管理(一)
- 内存管理(一)
- 内存管理一
- 内存的管理(一)
- 内存管理(一)
- Linux内存管理(一)
- C#内存管理一
- 内存管理一
- 内存管理(一)
- Linux内存管理(一)
- Linux内存管理 一
- 内存管理一
- iOS内存管理(一)
- iOS内存管理(一)
- xml 文件介绍
- 数据结构.平衡二叉树.从二叉排序树到平衡二叉树
- 全排列问题
- 用YACC构造简单语法分析器
- c++学习笔记
- android内存管理(一)
- Python 网页爬虫 & 文本处理 & 科学计算 & 机器学习 & 数据挖掘兵器库
- http协议(接触ajax时学习)
- Hibernate多对多之中间表只有两个外键做联合主键
- javaWeb servlet加载速度超级慢的问题
- call_once
- unity 球体表面平均分割点
- 当activity运行时添加fragment(fragment应用的第二种方法)
- Android-Property 动画自定义TypeEvaluator