您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 其它行业文档 > Dalvik虚拟机垃圾收集(GC)过程分析
Dalvik虚拟机垃圾收集(GC)过程分析前面我们分析了Dalvik虚拟机堆的创建过程,以及Java对象在堆上的分配过程。这些知识都是理解Dalvik虚拟机垃圾收集过程的基础。垃圾收集是一个复杂的过程,它要将那些不再被引用的对象进行回收。一方面要求Dalvik虚拟机能够标记出哪些对象是不再被引用的。另一方面要求Dalvik虚拟机尽快地回收内存,避免应用程序长时间停顿。本文就将详细分析Dalvik虚拟机是如何解决上述问题完成垃圾收集过程的。Dalvik虚拟机使用Mark-Sweep算法来进行垃圾收集。顾名思义,Mark-Sweep算法就是为Mark和Sweep两个阶段进行垃圾回收。其中,Mark阶段从根集(RootSet)开始,递归地标记出当前所有被引用的对象,而Sweep阶段负责回收那些没有被引用的对象。在分析Dalvik虚拟机使用的Mark-Sweep算法之前,我们先来了解一下什么情况下会触发GC。Dalvik虚拟机在三种情况下会触发四种类型的GC。每一种类型GC使用一个GcSpec结构体来描述,它的定义如下所示:[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片structGcSpec{/*Iftrue,onlytheapplicationheapisthreatened.*/boolisPartial;/*Iftrue,thetraceisrunconcurrentlywiththemutator.*/boolisConcurrent;/*Togglesforthesoftreferenceclearingpolicy.*/booldoPreserve;/*Anameforthisgarbagecollectionmode.*/constchar*reason;};这个结构体定义在文件dalvik/vm/alloc/Heap.h中。GcSpec结构体的各个成员变量的含义如下所示:isPartial:为true时,表示仅仅回收Active堆的垃圾;为false时,表示同时回收Active堆和Zygote堆的垃圾。isConcurrent:为true时,表示执行并行GC;为false时,表示执行非并行GC。doPreserve:为true时,表示在执行GC的过程中,不回收软引用引用的对象;为false时,表示在执行GC的过程中,回收软引用引用的对象。reason:一个描述性的字符串。Davlik虚拟机定义了四种类的GC,如下所示:[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片/*NotenoughspaceforanordinaryObjecttobeallocated.*/externconstGcSpec*GC_FOR_MALLOC;/*AutomaticGCtriggeredbyexceedingaheapoccupancythreshold.*/externconstGcSpec*GC_CONCURRENT;/*ExplicitGCviaRuntime.gc(),VMRuntime.gc(),orSIGUSR1.*/externconstGcSpec*GC_EXPLICIT;/*FinalattempttoreclaimmemorybeforethrowinganOOM.*/externconstGcSpec*GC_BEFORE_OOM;这四个全局变量声明在文件dalvik/vm/alloc/Heap.h中。它们的含义如下所示:GC_FOR_MALLOC:表示是在堆上分配对象时内存不足触发的GC。GC_CONCURRENT:表示是在已分配内存达到一定量之后触发的GC。GC_EXPLICIT:表示是应用程序调用System.gc、VMRuntime.gc接口或者收到SIGUSR1信号时触发的GC。GC_BEFORE_OOM:表示是在准备抛OOM异常之前进行的最后努力而触发的GC。实际上,GC_FOR_MALLOC、GC_CONCURRENT和GC_BEFORE_OOM三种类型的GC都是在分配对象的过程触发的。在前面一文,我们提到,Dalvik虚拟机在Java堆上分配对象的时候,在碰到分配失败的情况,会尝试调用函数gcForMalloc进行垃圾回收。函数gcForMalloc的实现如下所示:[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片staticvoidgcForMalloc(boolclearSoftReferences){......constGcSpec*spec=clearSoftReferences?GC_BEFORE_OOM:GC_FOR_MALLOC;dvmCollectGarbageInternal(spec);}这个函数定义在文件dalvik/vm/alloc/Heap.cpp中。参数clearSOftRefereces表示是否要对软引用引用的对象进行回收。如果要对软引用引用的对象进行回收,那么就表明当前内存是非常紧张的了,因此,这时候执行的就是GC_BEFORE_OOM类型的GC。否则的话,执行的就是GC_FOR_MALLOC类型的GC。它们都是通过调用函数dvmCollectGarbageInternal来执行的。在前面一文,我们也提到,当Dalvik虚拟机成功地在堆上分配一个对象之后,会检查一下当前分配的内存是否超出一个阀值,如下所示:[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片void*dvmHeapSourceAlloc(size_tn){......HeapSource*hs=gHs;Heap*heap=hs2heap(hs);if(heap-bytesAllocated+nhs-softLimit){......returnNULL;}void*ptr;if(gDvm.lowMemoryMode){......ptr=mspace_malloc(heap-msp,n);......}else{ptr=mspace_calloc(heap-msp,1,n);......}countAllocation(heap,ptr);......if(heap-bytesAllocatedheap-concurrentStartBytes){......dvmSignalCond(&gHs-gcThreadCond);}returnptr;}这个函数定义在文件dalvik/vm/alloc/HeapSource.cpp中。函数dvmHeapSourceAlloc成功地在Active堆上分配到一个对象之后,就会检查Active堆当前已经分配的内存(heap-bytesAllocated)是否大于预设的阀值(heap-concurrentStartBytes)。如果大于,那么就会通过条件变量gHs-gcThreadCond唤醒GC线程进行垃圾回收。预设的阀值(heap-concurrentStartBytes)是一个比指定的堆最小空闲内存小128K的数值。也就是说,当堆的空闲内不足时,就会触发GC_CONCURRENT类型的GC。GC线程是Dalvik虚拟机启动的过程中创建的,它的执行体函数是gcDaemonThread,实现如下所示:[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片staticvoid*gcDaemonThread(void*arg){dvmChangeStatus(NULL,THREAD_VMWAIT);dvmLockMutex(&gHs-gcThreadMutex);while(gHs-gcThreadShutdown!=true){booltrim=false;if(gHs-gcThreadTrimNeeded){intresult=dvmRelativeCondWait(&gHs-gcThreadCond,&gHs-gcThreadMutex,HEAP_TRIM_IDLE_TIME_MS,0);if(result==ETIMEDOUT){/*TimedoutwaitingforaGCrequest,scheduleaheaptrim.*/trim=true;}}else{dvmWaitCond(&gHs-gcThreadCond,&gHs-gcThreadMutex);}......dvmLockHeap();if(!gDvm.gcHeap-gcRunning){dvmChangeStatus(NULL,THREAD_RUNNING);if(trim){trimHeaps();gHs-gcThreadTrimNeeded=false;}else{dvmCollectGarbageInternal(GC_CONCURRENT);gHs-gcThreadTrimNeeded=true;}dvmChangeStatus(NULL,THREAD_VMWAIT);}dvmUnlockHeap();}dvmChangeStatus(NULL,THREAD_RUNNING);returnNULL;}这个函数定义在文件dalvik/vm/alloc/HeapSource.cpp中。GC线程平时没事的时候,就在条件变量gHs-gcThreadCond上进行等待HEAP_TRIM_IDLE_TIME_MS毫秒(5000毫秒)。如果在HEAP_TRIM_IDLE_TIME_MS毫秒内,都没有得到执行GC的通知,那么它就调用函数trimHeaps对Java堆进行裁剪,以便可以将堆上的一些没有使用到的内存交还给内核。函数trimHeaps的实现可以参考前面一文。否则的话,就会调用函数dvmCollectGarbageInternal进行类型为GC_CONCURRENT的GC。注意,函数gcDaemonThread在调用函数dvmCollectGarbageInternal进行类型为GC_CONCURRENT的GC之前,会先调用函数dvmLockHeap来锁定堆的。等到GC执行完毕,再调用函数dvmUnlockHeap来解除对堆的锁定。这与函数gcForMalloc调用函数dvmCollectGarbageInternal进行类型为GC_FOR_MALLOC和GC_CONCURRENT的GC是一样的。只不过,对堆进行锁定和解锁的操作是在调用堆栈上的函数dvmMalloc进行的,具体可以参考前面一文。当应用程序调用System.gc、VMRuntime.gc接口,或者接收到SIGUSR1信号时,最终会调用到函数dvmCollectGarbage,它的实现如下所示:[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片voiddvmCollectGarbage(){if(gDvm.disableExplicitGc){return;}dvmLockHeap();dvmWaitForConcurrentGcToComplete();dvmCollectGarbageInternal(GC_EXPLICIT);dvmUnlockHeap();}这个函数定义在文件dalvik/vm/alloc/Alloc.cpp中。如果Davik虚拟机在启动的时候,通过-XX:+DisableExplicitGC选项禁用了显式GC,那么函数dvmCollectGarbage什么也不做就返回了。这意味着Dalvik虚拟机可能会不支持应用程序显式的GC请求。一旦Dalvik虚拟机支持显式GC,那么函数dvmCollectGarbage就会先锁定堆,并且等待有可能正在执行的GC_CONCURRENT类型的GC完成之后,再调用函数dvmCollectGarbageInternal进行类型为GC_EXPLICIT的GC。以上就是三种情况下会触发四种类型的GC。有了这个背景知识之后,我们接下来就开始分析Dalvik虚拟机使用的
本文标题:Dalvik虚拟机垃圾收集(GC)过程分析
链接地址:https://www.777doc.com/doc-24022 .html