android内存管理了解_第1页
android内存管理了解_第2页
android内存管理了解_第3页
android内存管理了解_第4页
android内存管理了解_第5页
已阅读5页,还剩26页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、目录Low Memory KillerAshmemPmemdalvik虚拟机内存管理低内存管理器(低内存管理器(Low Memory Killer) 低内存管理器(Low Memory Killer) ,相对于Linux标准OOM(Out Of Memory)机制更加灵活,它可以根据需要杀死进程来释放需要的内存。 源代码位于drivers/staging/Android/lowmemorykiller.c 匿名共享内存(ashmem) ,为进程间提供大块共享内存,同时为内核提供回收和管理这个内存的机制。 源代码位于mm/ashmem.c Android PMEM(Physical) ,PMEM

2、用于向用户空间提供连续的物理内存区域,DSP和某些设备只能工作在连续的物理内存上。 源代码位于drivers/misc/pmem.c Low Memory Killer的实现的实现 Low Memory Killer的源代码在drivers/staging/android/lowmemorykiller.c中,它是通过注册Cache Shrinker来实现的。Cache Shrinker是标准linux kernel回收内存页面的一种机制,它由内核线程kswapd监控,当空闲内存页面不足时,kswapd会调用注册的Shrinker回调函数,来回收内存页面。 Low Memory Killer是

3、在模块初始化时注册Cache Shrinker的,代码如下: static int _init lowmem_init(void) register_shrinker(&lowmem_shrinker); / 注册 Cache Shrinker return 0; lowmem_shrinker的定义如下: static struct shrinker lowmem_shrinker = .shrink = lowmem_shrink, .seeks = DEFAULT_SEEKS * 16 ; register_shrinker 会将lowmem_shrink 加入Shrinker

4、List 中,被kswapd 在遍历Shrinker List 时调用,而Low Memory Killer 的功能就是在lowmem_shrink 中实现的。 lowmem_shrink 用两个数组作为选择用两个数组作为选择Bad 进程的依据,这两个数组的定义如下:进程的依据,这两个数组的定义如下: static int lowmem_adj6 = 0, 1, 6, 12, ; static int lowmem_adj_size = 4; static size_t lowmem_minfree6 = 3*512, / 6MB 2*1024, / 8MB 4*1024, / 16MB 16

5、*1024, / 64MB ; lowmem_shrink 首先计算当前空闲内存的大小,如果小于某个阈值,则以该阈值对应首先计算当前空闲内存的大小,如果小于某个阈值,则以该阈值对应的优先级为基准,遍历各个进程,计算每个进程占用内存的大小,找出优先级大于基准优的优先级为基准,遍历各个进程,计算每个进程占用内存的大小,找出优先级大于基准优先级的进程,在这些进程中选择优先级最大的杀死,如果优先级相同,则选择占用内存最先级的进程,在这些进程中选择优先级最大的杀死,如果优先级相同,则选择占用内存最多的进程。多的进程。 lowmem_shrink 杀死进程的方法是向进程发送一个不可以忽略或阻塞的杀死进程的

6、方法是向进程发送一个不可以忽略或阻塞的SIGKILL 信信号:号: force_sig(SIGKILL, selected); 用户接口用户接口 设置空闲内存阈值的接口:/sys/module/lowmemorykiller/parameters/minfree,设置对应优先级的接口:/sys/module/lowmemorykiller/parameters/adj,设置各个进程优先级的接口: /proc/oom_adj。 Android 启动时读取的配置文件/init.rc 中定义了相应的属性供AP 使用并有设置这些参数。 将init进程oom_adj设置为-16,从而保证init进程永远

7、不会被杀掉。 Ashmem相关介绍相关介绍 基本原理 Android 的Ashmem是一种共享内存的机制,它基于mmap系统调用,不同进程可以将同一段物理内存映射到各自的虚拟地址控制,从而实现共享。 Ashmem与mmap的区别 mmap通过映射同一个普通文件实现进程间共享内存,普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read,write等操作。进程在映射空间对共享内存的改变并不直接写回到磁盘文件中,在调用munmap后才执行此操作。可以通过调用msync实现磁盘上文件内存与共享内存区的内容一致。 Ashmem与mmap的区别在于Ashmem与cac

8、he shrinker关联起来,可以控制cache shrinker在适当时机回收这些共享内存。 Ashmem的实现的实现 Ashmem的源代码在mm/ashmem.c中,它通过注册Cache Shrinker回收内存,通过注册misc设备提供open,mmap等接口,mmap则通过tmpfs创建文件来分配内存,tmpfs将一块内存虚拟为一个文件,这样操作共享内存就相当于操作一个文件。 Ashmem用两个结构体ashmem_area和ashmem_range来维护分配的内存,ashmem_area代表共享的内存区域,ashmem_range则将这段区域以页为单位分为多个range。 ashme

9、m_area有个unpinned_list成员,挂在这个list上的range可以被回收。ashmem_range有一个LRU链表,在cache shrink回收一个ashmem_area的某段内存时候,是根据LRU的原则来选择哪些页面优先被回收的。 Ashmem的基本结构主要函数功能简单分析主要函数功能简单分析ashmem_init 这是module初始化函数,Ashmem是作为一个模块实现的。该函数主要功能: 调用kmem_cache_create分别创建struct ashmem_area和struct ashmem_range 的slab cache 调用misc_register注册

10、 ahsmem driver 调用register_shrinker注册Ashmem的 Cache Shrinker ashmem_open 标准misc设备的open函数。它调用kmem_cache_zalloc分配一个 ashmem_area,并初始化各成员变量。 ashmem_release 做与ashmem_open相反工作,释放tmpfs文件,ashmem_area及其ashmem_range。 ashmem_mmap mmap操作,主要就是调用shmem_file_setup从tmpfs文件系统中创建一个文件(实际上就 是一段RAM)给ashmem_area用,该文件代表着这段被共

11、享的内存。Ashmem真正实现进程共享内存的机制是靠shmem这个linux标准机制提供的。 主要函数功能简单分析主要函数功能简单分析ashmem_shrink 即Ashmem的cache shrink函数。它被mm/vmscan.c:shrink_slab调用,或者被用户的 ioctl 命令调用。这个函数从LRU链表上回收指定数目的 unpinned ashmem_range。 ashmem_ioctl 这个函数提供ioctl 接口,它实现了如下命令: 主要函数功能简单分析主要函数功能简单分析ashmem_unpin unpin一段内存。实现的方法很简单,就是分配一个ashmem_range

12、,把它挂到ashmem_area - unpinned_list上,并加到 LRU链表上。 ashmem_pin pin一段内存,从 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传给mma

13、p,获得同一块内存,handle在进程间的传递可通过Binder来实现。 Pmem相关介绍相关介绍 基本原理 Android Pmem是为了实现共享大尺寸连续物理内存而开发的一种机制,该机制对dsp,gpu等部件非常有用。Pmem相当于把系统内存划分出一部分单独管理,即不被linux mm管理,实际上linux mm根本看不到这段内存。 Pmem与Ashmem的区别 Pmem和Ashmem都通过mmap来实现共享内存,其区别在于Pmem的共享区域是一段连续的物理内存,而Ashmem的共享区域在虚拟空间是连续的,物理内存却不一定连续。dsp和某些设备只能工作在连续的物理内存上,这样cpu与dsp

14、之间的通信就需要通过Pmem来实现。 Pmem的实现的实现 Pmem的源代码在drivers/misc/pmem.c中,Pmem驱动依赖于linux的misc device和platform driver框架,一个系统可以有多个Pmem,默认的是最多10个。Pmem暴露4组操作,分别是platform driver的probe和remove操作; misc device的fops接口和vm_ops操作。模块初始化时会注册一个platform driver,在之后probe时,创建misc设备文件,分配内存,完成初始化工作。 Pmem通过pmem_info,pmem_data,pmem_regi

15、on三个结构体维护分配的共享内存,其中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

16、虚拟机内存管理虚拟机内存管理android模式linux模式dalvik虚拟机内存管理虚拟机内存管理 内存管理的核心就是两个部分:分配内存和回收内存。Java语言使用new操作符来分配内存,但是与C/C+等语言不同的是,Java语言并没有提供任何操作来释放内存,而是通过一种叫做垃圾收集的机制来回收内存。对于内存管理的实现,我们通过三个方面来加以分析:内存分配,内存回收和内存管理调试。对象布局对象布局 所有的对象都有一个相同的头部clazz和lock。(1)clazz:clazz指向该对象的类对象,类对象用来描述该对象所属的类,这样可以很容易的从一个对象获取该对象所属的类的具体信息。(2)loc

17、k:是一个无符号整数,用以实现对象的同步。(3)data:存放对象数据,根据对象的不同数据区的大小是不同的。内存管理的主要操作之一是为Java对象分配内存,Java对象在虚拟机中的内存布局如下: 堆堆 堆是dalvik虚拟机从操作系统分配的一块连续的虚拟内存。heapBase是堆的起始地址,heapLimit是堆的最大地址,堆大小的最大值可以通过-Xmx选项或dalvik.vm.heapsize指定。在原生系统中,一般dalvik.vm.heapsize值是32M,在MIUI中我们将其设为64M。 在dalvik虚拟机实现中,是通过底层的bionicC库的malloc/free操作来分配/释放

18、内存的。bionicC库的malloc/free操作是基于DougLea的实现(dlmalloc) 堆内存位图堆内存位图 在虚拟机中维护了两个对应于堆内存的位图,称为liveBits和markBits。 在对象布局中,我们看到对象最小占用8个字节。在为对象分配内存时要求必须8字节对齐。这也就是说,对象的大小会调整为8字节的倍数。比如说一个对象的实际大小是13字节,但是在分配内存的时候分配16字节。因此所有对象的起始地址一定是8字节的倍数。堆内存位图就是用来描述堆内存的,每一个bit描述8个字节,因此堆内存位图的大小是对的64分之一。对于MIUI的实现来说,这两个位图各占1M。 liveBits

19、的作用是用来跟踪堆中以分配的内存,每分配一个对象时,对象的内存起始地址对应于位图中的位被设为1。在下一篇垃圾收集中我们会进一步的分析liveBits和markBits这两个位图的作用。 dvmAllocObject dvmAllocObject在dalvik虚拟机中,new操作符最终对应dvmAllocObject这个C函数。下面通过伪码的形式列出dvmAllocObject的实现。Object*dvmAllocObject(ClassObject *clazz, int flags) n = get object size form class object clazz first try:

20、 allocate n bytes from heap if first try failed run garbage collector without collecting soft references second try: allocate n bytes from heap if second try failed third try: grow the heap and allocate n bytes from heap (注释:堆是虚拟内存,一开始并未分配所有的物理内存,只要还没有达到虚拟内存的最大值,可以通过获取更多物理内存的方式来扩展堆) if third try fai

21、led run garbage collector with collecting soft references fourth try: grow the hap and allocate n bytes from heap if fourth try failed, return null pointer, dalvik vm will abort可以看出,为了分配内存,虚拟机尽了最大的努力,做了四次尝试。其中进行了两次垃圾收集,第一次不收集SoftReference,第二次收集SoftReference。从中我们也可以看出垃圾收集的时机,实质上在dalvik虚拟机实现中有3个时机可以触发

22、垃圾收集的运行:(1)程序员显式的调用System.gc()(2)内存分配失败时(3)如果分配的对象大小超过384KB,运行并发标记(concurrent mark),垃圾回收垃圾回收 在dalvik虚拟机中,内存分配操作的流程相对比较简单直观,从一个堆中分配可用内存,分配失败时触发垃圾收集。 垃圾收集是dalvik虚拟机内存管理的核心,垃圾收集的性能在很大程度上影响了一个Java程序内存使用的效率。顾名思义,垃圾收集就是收集垃圾内存加以回收。dalvik虚拟机使用常用的Mark-Sweep算法,该算法一般分Mark阶段(标记出活动对象),Sweep阶段(回收垃圾内存)和可选的Compact阶

23、段(减少堆中的碎片)。dalvik虚拟机的实现不进行可选的Compact阶段。 Mark 垃圾收集的第一步是标记出活动对象,因为没有办法识别那些不可访问的对象(unreachableobjects),因此我们只能标记出活动对象,这样所有未被标记的对象就是可以回收的垃圾 1.1 根集合根集合(RootSet)当进行垃圾收集时,需要停止dalvik虚拟机的运行(当然,除了垃圾收集之外)。因此垃圾收集又被称作STW(stop-the-world,整个世界因我而停止)。dalvik虚拟机在运行过程中要维护一些状态信息,这些信息包括:每个线程所保存的寄存器,Java类中的静态字段,局部和全局的JNI引用

24、,JVM中的所有函数调用会对应一个相应C的栈帧。每一个栈帧里可能包含对对象的引用,比如包含对象引用的局部变量和参数。所有这些引用信息被加入到一个集合中,叫根集合。然后从根集合开始,递归的查找可以从根集合出发访问的对象。因此,Mark过程又被成为追踪,追踪所有可被访问的对象。如下图所示,假定从根集合a开始,我们可以访问的对象集合为a,b, c, d,这样就追踪出所有可被访问的对象集合。 (5.98 KB) 1.2 标记栈标记栈(MarkStack)垃圾收集使用栈来保存根集合,然后对栈中的每一个元素,递归追踪所有可访问的对象,对于所有可访问的对象,在markBits位图中该将对象的内存起始地址对应

25、的位设为1。这样当栈为空时,markBits位图就是所有可访问的对象集合。 Concurrent Mark(并发标记并发标记) 为了运行垃圾收集,需要停止虚拟机的运行,这可能会导致程序比较长时间的停顿。垃圾收集的主要工作位于Mark阶段,为了缩短停顿时间,dalvik虚拟机使用了concurrentmark技术。Concurrentmark引入一个单独的gc线程,由该线程去跟踪自己的根集合中所有可访问的对象,同时所有其它的线程也在运行。这也是concurrent一词的含义,但是为了回收内存,即运行Sweep阶段,必需停止虚拟机的运行。这会导入一个问题,即在gc线程mark对象的时候,其它线程的

26、运行又引入了新的访问对象。因此在Sweep阶段,又重新运行mark阶段,但是在这个阶段对于已经mark的对象可以不用继续递归追踪了。这样从一定程度上降低了程序停顿时间。 Sweep 垃圾收集的第二步就是回收内存,在Mark阶段通过markBits位图我们可以得到所有可访问的对象集合,而liveBits位图表示所有已经分配的对象集合。因此通过比较这两个位图,liveBits位图和markBits位图的差异就是所有可回收的对象集合。Sweep阶段调用free来释放这些内存给堆。HAL介绍介绍 Android HAL 架构规划:HAL 的目的是为了把 Android framework 与 Linu

27、x kernel 完整隔开HAL描述描述HAL 的过去 HAL 的现实状况 过去的 libhardware_legacy 作法,比较是传统的module方式,也就是将 *.so 档案当做透过直接函数呼叫的方式,来操作驱动程序。 当然,应用程序也可以不需要透过 JNI 的方式进行,直接以加载 *.so 檔(dlopen)的做法呼叫 *.so 里的符号(symbol)也是一种方式。 shared library来使用,在 runtime(JNI 部份)以 direct function call 使用 HAL module。现在的 libhardware 作法,就有stub的味道了。HAL stu

28、b 是一种代理人(proxy)的概念,stub 虽然仍是以 *.so 檔的形式存在,但 HAL 已经将 *.so 档隐藏起来了。Stub 向 HAL提供操作函数(operations),而 runtime 则是向 HAL 取得特定模块(stub)的 operations,再 callback 这些操作函数。这种以 indirect function call 的实作架构,让 HAL stub 变成是一种包含关系,即 HAL 里包含了许许多多的 stub(代理人)。Runtime 只要说明类型,即 module ID,就可以取得操作函数。 主要函数功能简单分析主要函数功能简单分析ashmem_i

29、nit 这是module初始化函数,Ashmem是作为一个模块实现的。该函数主要功能: 调用kmem_cache_create分别创建struct ashmem_area和struct ashmem_range 的slab cache 调用misc_register注册 ahsmem driver 调用register_shrinker注册Ashmem的 Cache Shrinker ashmem_open 标准misc设备的open函数。它调用kmem_cache_zalloc分配一个 ashmem_area,并初始化各成员变量。 ashmem_release 做与ashmem_open相反

30、工作,释放tmpfs文件,ashmem_area及其ashmem_range。 ashmem_mmap mmap操作,主要就是调用shmem_file_setup从tmpfs文件系统中创建一个文件(实际上就 是一段RAM)给ashmem_area用,该文件代表着这段被共享的内存。Ashmem真正实现进程共享内存的机制是靠shmem这个linux标准机制提供的。 主要函数功能简单分析主要函数功能简单分析ashmem_shrink 即Ashmem的cache shrink函数。它被mm/vmscan.c:shrink_slab调用,或者被用户的 ioctl 命令调用。这个函数从LRU链表上回收指定

31、数目的 unpinned ashmem_range。 ashmem_ioctl 这个函数提供ioctl 接口,它实现了如下命令: Pmem的实现的实现 Pmem的源代码在drivers/misc/pmem.c中,Pmem驱动依赖于linux的misc device和platform driver框架,一个系统可以有多个Pmem,默认的是最多10个。Pmem暴露4组操作,分别是platform driver的probe和remove操作; misc device的fops接口和vm_ops操作。模块初始化时会注册一个platform driver,在之后probe时,创建misc设备文件,分配内存,完成初始化工作。 Pmem通过pmem_info,pmem_data,pmem_region三个结构体维护分配的共享内存,其中pmem_info代表一个Pmem设备分配的内存块,pmem_data代表该内存块的一个子块,pmem_region则把每个子

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论