android内存管理
Posted xuguoli_beyondboy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android内存管理相关的知识,希望对你有一定的参考价值。
虚拟内存
如果在程序被挂起或被换出前仅仅使用了一部分进程快,那么为该进程给内存中装入太多的块显然会带来巨大的浪费。而虚拟内存借助磁盘和内存交换,仅仅装入这小部分块来更好地使用内存,然后,如果程序转移到或访问到不在内存中的某个快中的指令或数据时,就会引发一个中断,告诉操作系统读取需要的块。
我们知道进程中的所有内存访问都是逻辑地址,这些逻辑地址在运行时动态的被转换成物理地址,而这意味着一个进程可以被换入或换出内存,使得进程可以在执行过程中的不同时刻占据内存中的不同区域。还有一个进程可以划分成许多块,在执行过程中,这些块不需要连续的位于内存中。这两点也是虚拟内存突破所在。
接下来看虚拟内存重要的一些概念和组成
- 页号:每个进程内存都可以分割成好多块,每一块有对应的页号标记。
- 页框号:正真物理内存也相当于被分成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,
;
//这是默认一页为4kB
static int lowmem_adj_size = 4;
//默认定义了4个
//lowmem_minfree[i]*lowmem_adj_size=nMB
static 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);
//当内存较低且需要释放时,会初始化shrinker
static 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内存管理的主要内容,如果未能解决你的问题,请参考以下文章