Dalvik虚拟机Java堆创建过程分析.docx
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_05.gif)
《Dalvik虚拟机Java堆创建过程分析.docx》由会员分享,可在线阅读,更多相关《Dalvik虚拟机Java堆创建过程分析.docx(24页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。
1、Dalvik虚拟机Java堆创建过程分析使用C/C+开发应用程序最令头痛的问题就是内存管理。慎不留神,要么内存泄漏,要么内 存破坏。虚拟机要解决的问题之一就是帮助应用程序自动分配和释放内存。为了达到这个目 的,虚拟机在启动的时候向操作系统申请一大块内存当作对象堆。之后当应用程序创建对象 时,虚拟机就会在堆上分配合适的内存块。而当对象不再使用时,虚拟机就会将它占用的内 存块归还给堆。Dalvik虚拟机也不例外,本文就分析它的Java堆创建过程。从前面一文可以知道,在Dalvik虚拟机中,Java堆实际上是由一个Active堆和一个Zygote 堆组成的,如图1所示:Heap SourceActM
2、 HeapZygote HeapCard Table图1 Dalvi嘘拟机的Java堆其中,Zygote堆用来管理Zygote进程在启动过程中预加载和创建的各种对象,而Active堆 是在Zygote进程fork第一个子进程之前创建的。之后无论是Zygote进程还是其子进程,都 在Active堆上进行对象分配和释放。这样做的目的是使得Zygote进程和其子进程最大限度 地共享Zygote堆所占用的内存。为了管理Java堆,Dalvik虚拟机需要一些辅助数据结构,包括一个Card Table、两 个Heap Bitm叩和一个Mark Stacko Card Table是为了记录在垃圾收集过程中对
3、象的引用情 况的,以便可以实现Concurrent G。图1的两个Heap Bitmap, 一个称为Live Heap Bitmap, 用来记录上次GC之后,还存活的对象,另一个称为Mark Heap Bitmap,用来记录当前GC 中还存活的对象。这样,上次GC后存活的但是当前GC不存活的对象,就是需要释放的对 象。Davlk虚拟机使用标记-清除(Mark-Sweep)算法进行GC。在标记阶段,通过一个Mark Stack来实现递归检查被引用的对象,即在当前GC中存活的对象。有了这个Mark Stack, 就可以通过循环来模拟函数递归调用。Dalvik虚拟机Java堆的创建过程实际上就是上面
4、分析的各种数据结构的创建过程, 它们是在Dalvik虚拟机启动的过程中创建的。接下来,我们就详细分析这个过程。从前面一文可以知道,Dalvik虚拟机在启动的过程中,会通过调用函数dvmGcStartup 来创建Java堆,它的实现如下所示:limit:描述堆所使用的内存块的结束地址。brk:描述当前堆所分配的最大内存值。回到函数addlnitialHeap中,参数hs和msp指向的是在函数dvmHeapSourceStartup 中创建的HeapSource结构体和mspace内存对象,而参数maximumSize描述的Java堆的增 长上限值。通过函数addlnitialHeap的实现就可以
5、看出,Dalvik虚拟机在启动的时候,实际上 只创建了一个Heapo这个Heap就是我们在图1中所说的Active堆,它开始的时候管理的 是整个Java堆。但是在图1中,我们说Java堆实际上还包含有一个Zygote堆的,那么这个 Zygote堆是怎么来的呢?从前面一文可以知道,Zygote进程会通过调用函数forkAndSpecializeCommon来 fork子进程,其中与Dalvik虚拟机Java堆相关的逻辑如下所示:cpp view plain copy在CODE上查看代码片派生到我的代码片static pid_t forkAndSpecializeCommon(const u4*
6、args, bool isSystemServer) (pid_t pid;if (!dvmGcPreZygoteFork() pid = fork();if (pid = 0) else return pid;)这个函数定义在文件 dalvik/vm/nativc/dalvik_systcm_Zygotc.cpp 中。从这里就可以看出,Zygote进程在fork子进程之前,会调用函数 dvmGcPreZygoteFork来处理一下Dalvik虚拟机Java堆。接下来我们就看看函数 dvmGcPreZygoteFork 都做 了哪些事情。函数dvmGcPreZygoteFork的实现如下所示:
7、cpp view plain copy在CODE上查看代码片派生到我的代码片bool dvmGcPreZygoteFork() (return dvmHeapSourceStartupBeforeFork();)这个函数定义在文件dalvik/vm/alloc/Alloc.cpp中。函数 dvmGcPreZygoteFork只是简单地封装了对另外一个函数 dvmHeapSourceStartupBeforeFork的调用,后者的实现如下所示:cpp view plain copy在CODE上查看代码片派生到我的代码片 bool dvmHeapSourceStartupBeforeFork()(
8、HeapSource *hs = gHs; / use a local to avoid the implicit volatile”HS_BOILERPLATE();assert(gDvm.zygote);if (IgDvm.newZygoteHeapAllocated) /* Ensure heaps are trimmed to minimize footprint pre-fork.*/trimHeaps();/* Create a new heap for post-fork zygote allocations. We only* try once, even if it fail
9、s.*/ALOGV(nSplitting out new zygote heap);gDvm.newZygoteHeapAllocated = true;return addNewHeap(hs);) return true;)这个函数定义在文件 dalvik/vm/alloc/HeapSource.cpp 中。前面我们在分析函数dvmHeapSourceStartup的实现时提到,全局变量gHs指向的 是一个HeapSource结构体,它描述了 Dalvik虚拟机Java堆的信息。同时,gDvm也是一个 全局变量,它的类型为DvmGlobalso gDvm指向的DvmGlobals结构体的成
10、员变量 newZygoteHeapAllocated 的值被初始化为 false。 因此,当函数 dvmHeapSourceStartupBeforeFork第一次被调用时,它会先调用函数trimHeaps来将Java堆 中没有使用到的内存归还给系统,接着再调用函数addNcwHcap来创建一个新的Heap。这 个新的Heap就是图1所说的Zygote堆了。由于函数dvmHeapSourceStartupBeforeFork第一次被调用之后,gDvm指向的 DvmGlobals结构体的成员变量newZygoteHeapAllocated的值就会被修改为true,因此起到 的效果就是以后Zygo
11、te进程对函数dvmHeapSourceStartupBeforeFork的调用都是无用功。这也意味着Zygote进程只会在fork第一个子进程的时候,才会将Java堆划一分为二来管理。接下来我们就继续分析函数trimHeaps和addNewHeap的实现,以便更好地理解 Dalvik虚拟机是如何管理Java堆的。函数trimHeaps的实现如下所示:cpp view plain copy在CODE上查看代码片派生到我的代码片/* Return unused memory to the system if possible.*/static void trimHeaps()(HS_BOILER
12、PLATE();HeapSource *hs = gHs;size_t heapBytes = 0;for (size_t i = 0; i numHeaps; i+) Heap *heap = &hs-heapsi;/* Return the wilderness chunk to the system. */mspace_trim(heap-msp, 0);/* Return any whole free pages to the system. */mspace_inspect_all(heap-msp, releasePagesInRange, &heapBytes);)/* Same
13、 for the native heap. */dlmalloc_trim(0);size_t nativeBytes = 0;dlmalloc_inspect_all(releasePagesInRange, &nativeBytes);LOGD_HEAP(nmadvised %zd (GC) + %zd (native)= %zd total bytes”,heapBytes, nativeBytes, heapBytes + nativeBytes);)这个函数定义在文件 dalvik/vm/alloc/HeapSource.cpp 中。函数trimHeaps对Dalvik虚拟机使用的J
14、ava堆和默认Native堆做了同样的两件事 情。第一件事情是调用C库提供的函数mspace_trim/dlmalloc_trim来将没有使用到的虚 拟内存和物理内存归还给系统,这是通过系统调用mremap来实现的。第二件事情是调用C库提供的函数mspace_inspect_all/dlmalloc_inspect_all将不能 使用的内存碎片对应的物理内存归还给系统,这是通过系统调用madvise来实现的。注意,在此种情况下,只能归还无用的物理内存,而不能归还无用的虚拟内存。因为归还内存碎片 对应的虚拟内存会使得堆的整体虚拟地址不连续。函数addNewHeap的实现如下所示:cpp view
15、 plain copy在CODE上查看代码片派生到我的代码片 static bool addNewHeap(HeapSource *hs)(Heap heap;assert(hs != NULL);if (hs-numHeaps = HEAP_SOURCE_MAX_HEAP_COUNT) return false;)memset(&heap, 0, sizeof(heap);/* Heap storage comes from a common virtual memory reservation.* The new heap will start on the page after the
16、old heap.*/char *base = hs-heaps|O.brk;size_t overhead = base - hs-heapsO.base;assert(size_t)hs-heapsO.base & (SYSTEM_PAGE_SIZE - 1) = 0);if (overhead + hs-minFree = hs-maximumSize) return false;)size_t morecoreStart = SYSTEM_PAGE_SIZE;heap.maximumSize = hs-growthLimit - overhead;heap.concurrentStar
17、tBytes = hs-minFree - CONCURRENT_START;heap.base 二 base;heap.limit = heap.base + heap.maximumSize;heap.brk = heap.base + morecoreStart;if (!remapNewHeap(hs, &heap) return false;)heap.msp = createMspace(base, morecoreStart, hs-minFree);if (heap.msp = NULL) return false;/* Don!t let the soon-to-be-old
18、 heap grow any further.*/hs-heaps 0 .maximumSize = overhead;hs-heaps0.limit 二 base;mspace_set_footprint_limit(hs-heaps0.msp, overhead);/* Put the new heap in the list, at heaps0.* Shift existing heaps down.*/memmove(&hs-heaps 1 , &hs-heaps0, hs-numHeaps * sizeof(hs-heaps0);hs-heaps0 = heap;hs-numHea
19、ps+;return true;)这个函数定义在文件 dalvik/vm/alloc/HeapSource.cpp 中。函数addNewHeap所做的事情实际上就是将前面创建的Dalvik虚拟机Java堆一分 为二,得到两个Heap。在划分之前,HeadSource结构体hs只有一个Heap,如图2所示:overheadh$-heaps|O|.base hs*hedpsO|.bckhs-heapSO.Wnit图2 DaM嘘拟机Java堆一分为二之前接下来在未使用的Dalvi嘘拟机Java堆中创建另外一个Heap,如图3所示:hs*heapsO.lifnit- - overheadhs*heap
20、5O.basehs*hea ps 0.hpn.hrirheap.linMt图3在未使用的Dalvi嘘拟机Java堆中创建一个新的Heap最后调整HeadSource结构体hs的heaps数蛆j即交heaps和heaps1的值,结果 如图4所示:hs-beapsl.bmit hs-heapsO.base. 7 4.卜overheadhs-heapsl.base hs-beapsl.bdc hs-heapsO.brkhs-heapslOj.hrrwt图4 Dalvi嘘拟机Java堆一分为二之后 其中,heapsl就是我们在图1中所说的Zygote堆,而heapsO就是我们在图1中所说的Active
21、 堆。以后无论是Zygote进程,还是Zygote子进程,需要分配对象时,都在Active堆上进行。 这样就可以使得Zygote堆最大限度地在Zygote进程及其子进程中共享。这样我们就分析完了函数addlnitialHeap及其相关函数的实现,接下来我们继续分析 函数 dvmHeapBitmapInit 和 allocMarkStack 的实现。函数dvmHeapBitmapInit的实现如下所示:cpp view plain copy在CODE上查看代码片派生到我的代码片bool dvmHeapBitmapInit(HeapBitmap *hb, const void *base, siz
22、e_t maxSize, const char *name)(void *bits;size t bitsLen;assert(hb != NULL);assert(name != NULL);bitsLen = HB_OFFSET_TONDEX(maxSize) * sizeof(*hb-bits);bits = dvmAllocRegion(bitsLen, PROT_READ | PROT_WRITE, name);if (bits = NULL) ALOGE(nCould not mmap %zd-byte ashmem region %sH, bitsLen, name);retur
23、n false;)hb-bits = (unsigned long *)bits;hb-bitsLen = hb-allocLen = bitsLen;hb-base = (uintptr_t)base;hb-max = hb-base - 1;return true;)这个函数定义在文件 dalvik/vm/alloc/HeapBitmap.cpp 中。参数hb指向一个HeapBitmap结构体,这个结构体正是函数dvmHeapBitm叩Init要 进行初始化的。参数base和maxSize描述的是Java堆的起始地址和大小。另夕I、一个参数name 描述的是参数hb指向的HeapBitma
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Dalvik 虚拟机 Java 创建 过程 分析
![提示](https://www.deliwenku.com/images/bang_tan.gif)
限制150内