版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Android内存管理认识目录LowMemoryKillerAshmemPmemdalvik虚拟机内存管理低内存管理器(LowMemoryKiller)低内存管理器(LowMemoryKiller),相对于Linux标准OOM(OutOfMemory)机制更加灵活,它可以根据需要杀死进程来释放需要的内存。源代码位于drivers/staging/Android/lowmemorykiller.c匿名共享内存(ashmem),为进程间提供大块共享内存,同时为内核提供回收和管理这个内存的机制。源代码位于mm/ashmem.cAndroidPMEM(Physical),PMEM用于向用户空间提供连续的物理内存区域,DSP和某些设备只能工作在连续的物理内存上。源代码位于drivers/misc/pmem.c
LowMemoryKiller的实现
LowMemoryKiller的源代码在drivers/staging/android/lowmemorykiller.c中,它是通过注册CacheShrinker来实现的。CacheShrinker是标准linuxkernel回收内存页面的一种机制,它由内核线程kswapd监控,当空闲内存页面不足时,kswapd会调用注册的Shrinker回调函数,来回收内存页面。LowMemoryKiller是在模块初始化时注册CacheShrinker的,代码如下:staticint__initlowmem_init(void){register_shrinker(&lowmem_shrinker);//注册CacheShrinkerreturn0;} lowmem_shrinker的定义如下:staticstructshrinkerlowmem_shrinker={.shrink=lowmem_shrink,.seeks=DEFAULT_SEEKS*16}; register_shrinker会将lowmem_shrink加入ShrinkerList中,被kswapd在遍历ShrinkerList时调用,而LowMemoryKiller的功能就是在lowmem_shrink中实现的。
lowmem_shrink用两个数组作为选择Bad进程的依据,这两个数组的定义如下:
staticintlowmem_adj[6]={
0,
1,
6,
12,
};
staticintlowmem_adj_size=4;
staticsize_tlowmem_minfree[6]={
3*512,//6MB
2*1024,//8MB
4*1024,//16MB
16*1024,//64MB
};
lowmem_shrink首先计算当前空闲内存的大小,如果小于某个阈值,则以该阈值对应的优先级为基准,遍历各个进程,计算每个进程占用内存的大小,找出优先级大于基准优先级的进程,在这些进程中选择优先级最大的杀死,如果优先级相同,则选择占用内存最多的进程。
lowmem_shrink杀死进程的方法是向进程发送一个不可以忽略或阻塞的SIGKILL信号:
force_sig(SIGKILL,selected);
用户接口
设置空闲内存阈值的接口:/sys/module/lowmemorykiller/parameters/minfree,设置对应优先级的接口:/sys/module/lowmemorykiller/parameters/adj,设置各个进程优先级的接口:/proc/<进程pid>/oom_adj。Android启动时读取的配置文件/init.rc中定义了相应的属性供AP使用并有设置这些参数。将init进程oom_adj设置为-16,从而保证init进程永远不会被杀掉。Ashmem的基本结构主要函数功能简单分析
ashmem_init这是module初始化函数,Ashmem是作为一个模块实现的。该函数主要功能:调用kmem_cache_create分别创建structashmem_area和structashmem_range的slabcache
调用misc_register注册ahsmemdriver
调用register_shrinker注册Ashmem的CacheShrinkerashmem_open标准misc设备的open函数。它调用kmem_cache_zalloc分配一个ashmem_area,并初始化各成员变量。
ashmem_release做与ashmem_open相反工作,释放tmpfs文件,ashmem_area及其ashmem_range。
ashmem_mmapmmap操作,主要就是调用shmem_file_setup从tmpfs文件系统中创建一个文件(实际上就是一段RAM)给ashmem_area用,该文件代表着这段被共享的内存。Ashmem真正实现进程共享内存的机制是靠shmem这个linux标准机制提供的。
主要函数功能简单分析ashmem_shrink即Ashmem的cacheshrink函数。它被mm/vmscan.c::shrink_slab调用,或者被用户的ioctl命令调用。这个函数从LRU链表上回收指定数目的unpinnedashmem_range。
ashmem_ioctl这个函数提供ioctl接口,它实现了如下命令:主要函数功能简单分析ashmem_unpinunpin一段内存。实现的方法很简单,就是分配一个ashmem_range,把它挂到ashmem_area->unpinned_list上,并加到LRU链表上。ashmem_pinpin一段内存,从ashmem_area->unpinned_list上拿下这个ashmem_range,由此可知,被unpin的range才能被回收,pin的range则不能回收。用户接口Ashmem驱动创建了/dev/ashmem设备文件,进程A可通过open打开该文件,用ioctl命令ASHMEM_SET_NAME和ASHMEM_SET_SIZE设置共享内存块的名字和大小,并将得到的handle传给mmap,来获得共享的内存区域,进程B通过将相同的handle传给mmap,获得同一块内存,handle在进程间的传递可通过Binder来实现。Pmem相关介绍基本原理AndroidPmem是为了实现共享大尺寸连续物理内存而开发的一种机制,该机制对dsp,gpu等部件非常有用。Pmem相当于把系统内存划分出一部分单独管理,即不被linuxmm管理,实际上linuxmm根本看不到这段内存。Pmem与Ashmem的区别Pmem和Ashmem都通过mmap来实现共享内存,其区别在于Pmem的共享区域是一段连续的物理内存,而Ashmem的共享区域在虚拟空间是连续的,物理内存却不一定连续。dsp和某些设备只能工作在连续的物理内存上,这样cpu与dsp之间的通信就需要通过Pmem来实现。Pmem的实现
Pmem的源代码在drivers/misc/pmem.c中,Pmem驱动依赖于linux的miscdevice和platformdriver框架,一个系统可以有多个Pmem,默认的是最多10个。Pmem暴露4组操作,分别是platformdriver的probe和remove操作;miscdevice的fops接口和vm_ops操作。模块初始化时会注册一个platformdriver,在之后probe时,创建misc设备文件,分配内存,完成初始化工作。Pmem通过pmem_info,pmem_data,pmem_region三个结构体维护分配的共享内存,其中pmem_info代表一个Pmem设备分配的内存块,pmem_data代表该内存块的一个子块,pmem_region则把每个子块分成多个区域。pmem_data是分配的基本单位,即每次应用层要分配一块Pmem内存,就会有一个pmem_data来表示这个被分配的内存块,实际上在open的时候,并不是open一个pmem_info表示的整个Pmem内存块,而是创建一个pmem_data以备使用。一个应用可以通过ioctl来分配pmem_data中的一个区域,并可以把它map到进程空间;并不一定每次都要分配和map整个pmem_data内存块。dalvik虚拟机内存管理android模式linux模式dalvik虚拟机内存管理内存管理的核心就是两个部分:分配内存和回收内存。Java语言使用new操作符来分配内存,但是与C/C++等语言不同的是,Java语言并没有提供任何操作来释放内存,而是通过一种叫做垃圾收集的机制来回收内存。对于内存管理的实现,我们通过三个方面来加以分析:内存分配,内存回收和内存管理调试。对象布局
所有的对象都有一个相同的头部clazz和lock。
(1)clazz:clazz指向该对象的类对象,类对象用来描述该对象所属的类,这样可以很容易的从一个对象获取该对象所属的类的具体信息。
(2)lock:是一个无符号整数,用以实现对象的同步。
(3)data:存放对象数据,根据对象的不同数据区的大小是不同的。
内存管理的主要操作之一是为Java对象分配内存,Java对象在虚拟机中的内存布局如下:
堆
堆是dalvik虚拟机从操作系统分配的一块连续的虚拟内存。heapBase是堆的起始地址,heapLimit是堆的最大地址,堆大小的最大值可以通过-Xmx选项或dalvik.vm.heapsize指定。在原生系统中,一般dalvik.vm.heapsize值是32M,在MIUI中我们将其设为64M。
在dalvik虚拟机实现中,是通过底层的bionicC库的malloc/free操作来分配/释放内存的。bionicC库的malloc/free操作是基于DougLea的实现(dlmalloc)
堆内存位图
在虚拟机中维护了两个对应于堆内存的位图,称为liveBits和markBits。
在对象布局中,我们看到对象最小占用8个字节。在为对象分配内存时要求必须8字节对齐。这也就是说,对象的大小会调整为8字节的倍数。比如说一个对象的实际大小是13字节,但是在分配内存的时候分配16字节。因此所有对象的起始地址一定是8字节的倍数。堆内存位图就是用来描述堆内存的,每一个bit描述8个字节,因此堆内存位图的大小是对的64分之一。对于MIUI的实现来说,这两个位图各占1M。
liveBits的作用是用来跟踪堆中以分配的内存,每分配一个对象时,对象的内存起始地址对应于位图中的位被设为1。在下一篇垃圾收集中我们会进一步的分析liveBits和markBits这两个位图的作用。
dvmAllocObjectdvmAllocObject在dalvik虚拟机中,new操作符最终对应dvmAllocObject这个C函数。下面通过伪码的形式列出dvmAllocObject的实现。
Object*dvmAllocObject(ClassObject*clazz,intflags){
n=getobjectsizeformclassobjectclazz
firsttry:allocatenbytesfromheap
iffirsttryfailed{
rungarbagecollectorwithoutcollectingsoftreferences
secondtry:allocatenbytesfromheap
}
ifsecondtryfailed{
thirdtry:growtheheapandallocatenbytesfromheap
(注释:堆是虚拟内存,一开始并未分配所有的物理内存,只要还没有达到虚拟内存的最大值,可以通过获取更多物理内存的方式来扩展堆)
}
ifthirdtryfailed{
rungarbagecollectorwithcollectingsoftreferences
fourthtry:growthehapandallocatenbytesfromheap
}
iffourthtryfailed,returnnullpointer,dalvikvmwillabort
}
可以看出,为了分配内存,虚拟机尽了最大的努力,做了四次尝试。其中进行了两次垃圾收集,第一次不收集SoftReference,第二次收集SoftReference。从中我们也可以看出垃圾收集的时机,实质上在dalvik虚拟机实现中有3个时机可以触发垃圾收集的运行:
(1)程序员显式的调用System.gc()
(2)内存分配失败时
(3)如果分配的对象大小超过384KB,运行并发标记(concurrentmark),
垃圾回收在dalvik虚拟机中,内存分配操作的流程相对比较简单直观,从一个堆中分配可用内存,分配失败时触发垃圾收集。
垃圾收集是dalvik虚拟机内存管理的核心,垃圾收集的性能在很大程度上影响了一个Java程序内存使用的效率。顾名思义,垃圾收集就是收集垃圾内存加以回收。dalvik虚拟机使用常用的Mark-Sweep算法,该算法一般分Mark阶段(标记出活动对象),Sweep阶段(回收垃圾内存)和可选的Compact阶段(减少堆中的碎片)。dalvik虚拟机的实现不进行可选的Compact阶段。
Mark
垃圾收集的第一步是标记出活动对象,因为没有办法识别那些不可访问的对象(unreachableobjects),因此我们只能标记出活动对象,这样所有未被标记的对象就是可以回收的垃圾
1.1根集合(RootSet)
当进行垃圾收集时,需要停止dalvik虚拟机的运行(当然,除了垃圾收集之外)。因此垃圾收集又被称作STW(stop-the-world,整个世界因我而停止)。dalvik虚拟机在运行过程中要维护一些状态信息,这些信息包括:每个线程所保存的寄存器,Java类中的静态字段,局部和全局的JNI引用,JVM中的所有函数调用会对应一个相应C的栈帧。每一个栈帧里可能包含对对象的引用,比如包含对象引用的局部变量和参数。
所有这些引用信息被加入到一个集合中,叫根集合。然后从根集合开始,递归的查找可以从根集合出发访问的对象。因此,Mark过程又被成为追踪,追踪所有可被访问的对象。如下图所示,假定从根集合{a}开始,我们可以访问的对象集合为{a,b,c,d},这样就追踪出所有可被访问的对象集合。
(5.98KB)
1.2标记栈(MarkStack)
垃圾收集使用栈来保存根集合,然后对栈中的每一个元素,递归追踪所有可访问的对象,对于所有可访问的对象,在markBits位图中该将对象的内存起始地址对应的位设为1。这样当栈为空时,markBits位图就是所有可访问的对象集合。ConcurrentMark(并发标记)
为了运行垃圾收集,需要停止虚拟机的运行,这可能会导致程序比较长时间的停顿。垃圾收集的主要工作位于Mark阶段,为了缩短停顿时间,dalvik虚拟机使用了concurrentmark技术。Concurrentmark引入一个单独的gc线程,由该线程去跟踪自己的根集合中所有可访问的对象,同时所有其它的线程也在运行。这也是concurrent一词的含义,但是为了回收内存,即运行Sweep阶段,必需停止虚拟机的运行。这会导入一个问题,即在gc线程mark对象的时候,其它线程的运行又引入了新的访问对象。因此在Sweep阶段,又重新运行mark阶段,但是在这个阶段对于已经mark的对象可以不用继续递归追踪了。这样从一定程度上降低了程序停顿时间。
Sweep垃圾收集的第二步就是回收内存,在Mark阶段通过markBit
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年度精密焊管产品研发与生产合同
- 二零二四年度企业租赁与管理合同
- 中国地质大学(武汉)《首饰设计及工艺概论》2022-2023学年第一学期期末试卷
- 中国地质大学(武汉)《景观修复设计》2023-2024学年第一学期期末试卷
- 八皇后课程设计报告
- 2024年度坯布织造加工与环保认证服务合同3篇
- 2024年标准外包装修施工协议样本版B版
- 2024年教育机构联合运营协议3篇
- 2024年创新舞台设计租赁协议3篇
- 2024农业机械作业与农业产业链延伸服务合同3篇
- 国开机考答案-工程数学(本)(闭卷)
- 城市生命线安全风险综合监测预警平台解决方案
- 《化妆品配方与制备技术》课件-第10章 唇部美容化妆品
- 交响音乐赏析智慧树知到期末考试答案章节答案2024年西安交通大学
- 企业EHS风险管理基础智慧树知到期末考试答案章节答案2024年华东理工大学
- 行政复议法-形考作业1-国开(ZJ)-参考资料
- 学校考核物业表格
- 《蝴蝶兰组培》课件
- HJ 194-2017 环境空气质量手工监测技术规范(正式版)
- 中西文化鉴赏智慧树知到期末考试答案2024年
- JT-397-1999港口危险货物集装箱安全管理规程-PDF解密
评论
0/150
提交评论