最近遇到了一个dalvik memory相关的问题,对dalvik的heap管理部分做了一些了解,在此总结一下,主要是下面3个方面的内容:
1,java heap的实现,和native heap的差异。
2,gc的实现。
3,配置heap的相关prop的含义。

davlik java heap


java的heap一般是和Native heap独立开来,因为java对象的回收,很多时候需要涉及到对整个内存的扫描,所以不会把native和java混杂在一起管理。
dalvik的heap相关有下面几个关键的类,其中DvmGlobals有一个全局的对象gDvm,这个对象是davlik中关键的一个对象,全局的信息都是保存在这里。

浏览这几个类相关的调用,可以看到heap的创建主要在dvmHeapSourceStartup()这个函数中,主要是下面几个步骤:
1,通过mmap映射了内存(按照最大的heapsize)到当前进程中(函数dvmAllocRegion())。
2,将mmap出来的地址交给dlmalloc来管理(通过mspace接口,create_mspace_with_base()),初始只给了startSize大小的内存给dlmalloc来管理。
3,调用addInitialHeap()建立了当前的Heap对象。

从代码中可以看出,java heap和native heap管理的确是独立的,java heap不会直接用的libc的库,而是单独mmap了一块内存出来,用dlmalloc的另外一套
接口来管理申请,释放操作。

另外,通过代码我们知道HeapSource中有两个Heap,第一个Heap是在dvmHeapSourceStartup中建立了一个heap,另外一次就是在dvmHeapSourceStartupBeforeFork()函数中调用了addNewHeap()有建立了一个新的Heap对象。
问题是为什么我们需要建立两个Heap?从Heap的结构上看,用一个heap也足以表达所有的信息了,这个问题留到后面再来讨论。


GC


dalvik采用的gc策略是mark-sweep。
mark-sweep的机制简单来说,就是扫描所有的object,把对象分成可以访问到,和不可访问到的,不可访问到的对象就是需要回收的对象。

HeapBitmap
在gc的时候,首先要考虑的就是,如何记录所有的object,然后去检查他的可访问性。
dalvik中使用HeapBitmap来实现的,HeapBitmap中的每个bit,对应这个java heap上的一个8Bytes的chunk。HeapBitmap上bit是1还是0,来表示对应的Heap上的内存是否对应着一个java object,基于此再去检测所有object的可访问性。
HeapBitmap是在dvmHeapSourceStartup()中调用dvmHeapBitmapInit()来初始化,他的内存也是通过mmap映射进来的。

liveBits&markBits
在gc的时候,主要操作了两个HeapBitmap对象,分别是gHs->liveBits和gHs->markBits。
liveBits,他是用于记录所有的存在的Java object,当一个object被申请的时候把对应的bit位设1(countAlloction()),free一个object的时候把他对应的bit设0(countFree())。
markBits是当做一个局部的变量来使用的,每次gc开始时所有的bit都是0,通过扫描所有的root object来记录所有的可访问到的object(把对应的markBits设置为1)。
之后比较liveBits和markBits,在liveBits中为1,markBits中为0的object就是需要回收的java object。

why need two Heaps in HeapSource?
现在回头去看为什么heapSource里面有两个Heap的对象?
我们在gc的流程中看到,gc的spec中有一个isPartial的条件,表明本次gc是否只做部分的gc来减少等待(在gcForMalloc的时候会设定isPartial)。这边的partial就是只scan当前的HeapSource中的heap[0]所对应的memory,而不去检测heap[1]对应的memory,这部分memory对应的bits直接从liveBits中copy到markBits中去,所以我们认为分为两个heap的主要目的应该就是为了实现Partial scan。
另外,从两个heap创建的时间来看,对于一般的app来说,先创建的heap主要在zygote中preload的class和resource时候使用了,这部分内存比较固定,比较少产生变化,而第二个heap是在fork app前创建的,这部分内存申请释放是比较频繁的,partical scan的时候回收第二个heap是可以回收出较多内存的。

 

heap 配置 prop


dalvik.vm.heapsize
Java heap的最大size,他确定了heap创建时候mmap进来的内存的大小。
对于应用程序来说,就是heap的上限。

dalvik.vm.heapstartsize
dvm startup时候建立的第一个heap的大小,一般不需要很大,主要for zygote去preload的,对于app而言,这个值没什么意义。
网上的说法是说这个是给app用的第一个heap的大小,实际不正确,app在启动的时候,两个heap都已经建立好了。

dalvik.vm.heapgrowthlimit
这个值是建立第二个heap的时候默认的上限(包含了第一个heap的size),这个值可以大一些,尽量满足一般app的需求,但是对于小内存的
机器,还是尽量小一些来节省内存。
对于某些app设定了android:largeHeap=true的属性,那么growthlimit实际无效,和heapSize相等了(见函数dvmClearGrowthLimit())。

dalvik.vm.heaptargetutilizatio,dalvik.vm.heapminfree,dalvik.vm.heapmaxfree
我们前面说到java heap是首先从/dev/zero中mmap出来一块内存(虚拟内存,并没有指向物理内存),然后逐步把这些内存交给dlmalloc来管理。
这3个prop就是和过程相关,3个值一起设定了一个softlimit的值(见函数setIdealFootprint()),尽量去保证heap的使用率,让内存不会被浪费,每次申请内存的时候,如果触及了softlimit首先会去做gc,在gc后还是无法申请成功的情况下,才去扩展dlmalloc管理的内存区域(见dvmHeapSourceAllocAndGrow())。
这3个prop的含义可以这么理解:
heaptargetutilization是当前dlmalloc管理的内存的理想的使用率(malloc_size / cur_heap_size )
maxfree是当前dlmalloc管理的内存最多可以有的free memory
minfree是当前dlmalloc管理的内存最少需要的free memory

posted on 2014-07-28 17:07  yards  阅读(2002)  评论(1编辑  收藏  举报