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

0 0
原创粉丝点击