




已阅读5页,还剩49页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Dalvik虚拟机垃圾收集(GC)过程分析前面我们分析了Dalvik虚拟机堆的创建过程,以及Java对象在堆上的分配过程。这些知识都是理解Dalvik虚拟机垃圾收集过程的基础。垃圾收集是一个复杂的过程,它要将那些不再被引用的对象进行回收。一方面要求Dalvik虚拟机能够标记出哪些对象是不再被引用的。另一方面要求Dalvik虚拟机尽快地回收内存,避免应用程序长时间停顿。本文就将详细分析Dalvik虚拟机是如何解决上述问题完成垃圾收集过程的。Dalvik虚拟机使用Mark-Sweep算法来进行垃圾收集。顾名思义,Mark-Sweep算法就是为Mark和Sweep两个阶段进行垃圾回收。其中,Mark阶段从根集(Root Set)开始,递归地标记出当前所有被引用的对象,而Sweep阶段负责回收那些没有被引用的对象。在分析Dalvik虚拟机使用的Mark-Sweep算法之前,我们先来了解一下什么情况下会触发GC。 Dalvik虚拟机在三种情况下会触发四种类型的GC。每一种类型GC使用一个GcSpec结构体来描述,它的定义如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片struct GcSpec /* If true, only the application heap is threatened. */ bool isPartial; /* If true, the trace is run concurrently with the mutator. */ bool isConcurrent; /* Toggles for the soft reference clearing policy. */ bool doPreserve; /* A name for this garbage collection mode. */ const char *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 view plain copy 在CODE上查看代码片派生到我的代码片/* Not enough space for an ordinary Object to be allocated. */ extern const GcSpec *GC_FOR_MALLOC; /* Automatic GC triggered by exceeding a heap occupancy threshold. */ extern const GcSpec *GC_CONCURRENT; /* Explicit GC via Runtime.gc(), VMRuntime.gc(), or SIGUSR1. */ extern const GcSpec *GC_EXPLICIT; /* Final attempt to reclaim memory before throwing an OOM. */ extern const GcSpec *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 view plain copy 在CODE上查看代码片派生到我的代码片static void gcForMalloc(bool clearSoftReferences) . const GcSpec *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 view plain copy 在CODE上查看代码片派生到我的代码片void* dvmHeapSourceAlloc(size_t n) . HeapSource *hs = gHs; Heap* heap = hs2heap(hs); if (heap-bytesAllocated + n hs-softLimit) . return NULL; void* ptr; if (gDvm.lowMemoryMode) . ptr = mspace_malloc(heap-msp, n); . else ptr = mspace_calloc(heap-msp, 1, n); . countAllocation(heap, ptr); . if (heap-bytesAllocated heap-concurrentStartBytes) . dvmSignalCond(&gHs-gcThreadCond); return ptr; 这个函数定义在文件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 view plain copy 在CODE上查看代码片派生到我的代码片static void *gcDaemonThread(void* arg) dvmChangeStatus(NULL, THREAD_VMWAIT); dvmLockMutex(&gHs-gcThreadMutex); while (gHs-gcThreadShutdown != true) bool trim = false; if (gHs-gcThreadTrimNeeded) int result = dvmRelativeCondWait(&gHs-gcThreadCond, &gHs-gcThreadMutex, HEAP_TRIM_IDLE_TIME_MS, 0); if (result = ETIMEDOUT) /* Timed out waiting for a GC request, schedule a heap trim. */ 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); return NULL; 这个函数定义在文件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 view plain copy 在CODE上查看代码片派生到我的代码片void dvmCollectGarbage() 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虚拟机使用的Mark-Sweep算法,整个过程如图1所示:图1 Dalvik虚拟机垃圾收集过程 Dalvik虚拟机支持非并行和并行两种GC。在图1中,左边是非并行GC的执行过程,而右边是并行GC的执行过程。它们的总体流程是相似的,主要差别在于前者在执行的过程中一直是挂起非GC线程的,而后者是有条件地挂起非GC线程。 由前面的分析可以知道,无论是并行GC,还是非并行GC,它们都是通过函数dvmCollectGarbageInternal来执行的。函数dvmCollectGarbageInternal的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void dvmCollectGarbageInternal(const GcSpec* spec) . if (gcHeap-gcRunning) . return; . gcHeap-gcRunning = true; . dvmSuspendAllThreads(SUSPEND_FOR_GC); . if (!dvmHeapBeginMarkStep(spec-isPartial) . dvmAbort(); dvmHeapMarkRootSet(); . if (spec-isConcurrent) . dvmClearCardTable(); dvmUnlockHeap(); dvmResumeAllThreads(SUSPEND_FOR_GC); . dvmHeapScanMarkedObjects(); if (spec-isConcurrent) . dvmLockHeap(); . dvmSuspendAllThreads(SUSPEND_FOR_GC); . dvmHeapReMarkRootSet(); . dvmHeapReScanMarkedObjects(); dvmHeapProcessReferences(&gcHeap-softReferences, spec-doPreserve = false, &gcHeap-weakReferences, &gcHeap-finalizerReferences, &gcHeap-phantomReferences); . dvmHeapSweepSystemWeaks(); . dvmHeapSourceSwapBitmaps(); . if (spec-isConcurrent) dvmUnlockHeap(); dvmResumeAllThreads(SUSPEND_FOR_GC); . dvmHeapSweepUnmarkedObjects(spec-isPartial, spec-isConcurrent, &numObjectsFreed, &numBytesFreed); . dvmHeapFinishMarkStep(); if (spec-isConcurrent) dvmLockHeap(); . dvmHeapSourceGrowForUtilization(); . gcHeap-gcRunning = false; . if (spec-isConcurrent) . dvmBroadcastCond(&gDvm.gcHeapCond); if (!spec-isConcurrent) dvmResumeAllThreads(SUSPEND_FOR_GC); . . dvmEnqueueClearedReferences(&gDvm.gcHeap-clearedReferences); . 这个函数定义在文件dalvik/vm/alloc/Heap.cpp中。 前面提到,在调用函数dvmCollectGarbageInternal之前,堆是已经被锁定了的,因此这时候任何需要堆上分配对象的线程都会被挂起。注意,这不会影响到那些不需要在堆上分配对象的线程。 在图1中显示的GC流程中,除了第一步的Lock Heap和最后一步的Unlock Heap,都对应于函数dvmCollectGarbageInternal的实现,它的执行逻辑如下所示: 第1步到第3步用于并行和非并行GC: 1. 调用函数dvmSuspendAllThreads挂起所有的线程,以免它们干扰GC。 2. 调用函数dvmHeapBeginMarkStep初始化Mark Stack,并且设定好GC范围。关于Mark Stack的介绍,可以参考前面一文。 3. 调用函数dvmHeapMarkRootSet标记根集对象。 第4到第6步用于并行GC: 4. 调用函数dvmClearCardTable清理Card Table。关于Card Table的介绍,可以参考前面一文。因为接下来我们将会唤醒第1步挂起的线程。并且使用这个Card Table来记录那些在GC过程中被修改的对象。 5. 调用函数dvmUnlock解锁堆。这个是针对调用函数dvmCollectGarbageInternal执行GC前的堆锁定操作。 6. 调用函数dvmResumeAllThreads唤醒第1步挂起的线程。 第7步用于并行和非并行GC: 7. 调用函数dvmHeapScanMarkedObjects从第3步获得的根集对象开始,归递标记所有被根集对象引用的对象。 第8步到第11步用于并行GC: 8. 调用函数dvmLockHeap重新锁定堆。这个是针对前面第5步的操作。 9. 调用函数dvmSuspendAllThreads重新挂起所有的线程。这个是针对前面第6步的操作。 10. 调用函数dvmHeapReMarkRootSet更新根集对象。因为有可能在第4步到第6步的执行过程中,有线程创建了新的根集对象。 11. 调用函数dvmHeapReScanMarkedObjects归递标记那些在第4步到第6步的执行过程中被修改的对象。这些对象记录在Card Table中。 第12步到第14步用于并行和非并行GC: 12. 调用函数dvmHeapProcessReferences处理那些被软引用(Soft Reference)、弱引用(Weak Reference)和影子引用(Phantom Reference)引用的对象,以及重写了finalize方法的对象。这些对象都是需要特殊处理的。 13. 调用函数dvmHeapSweepSystemWeaks回收系统内部使用的那些被弱引用引用的对象。 14. 调用函数dvmHeapSourceSwapBitmaps交换Live Bitmap和Mark Bitmap。执行了前面的13步之后,所有还被引用的对象在Mark Bitmap中的bit都被设置为1。而Live Bitmap记录的是当前GC前还被引用着的对象。通过交换这两个Bitmap,就可以使得当前GC完成之后,使得Live Bitmap记录的是下次GC前还被引用着的对象。 第15步和第16步用于并行GC: 15. 调用函数dvmUnlock解锁堆。这个是针对前面第8步的操作。 16. 调用函数dvmResumeAllThreads唤醒第9步挂起的线程。 第17步和第18步用于并行和非并行GC: 17. 调用函数dvmHeapSweepUnmarkedObjects回收那些没有被引用的对象。没有被引用的对象就是那些在执行第14步之前,在Live Bitmap中的bit设置为1,但是在Mark Bitmap中的bit设置为0的对象。 18. 调用函数dvmHeapFinishMarkStep重置Mark Bitmap以及Mark Stack。这个是针对前面第2步的操作。 第19步用于并行GC: 19. 调用函数dvmLockHeap重新锁定堆。这个是针对前面第15步的操作。 第20步用于并行和非并行GC: 20. 调用函数dvmHeapSourceGrowForUtilization根据设置的堆目标利用率调整堆的大小。 第21步用于并行GC: 21. 调用函数dvmBroadcastCond唤醒那些等待GC执行完成再在堆上分配对象的线程。 第22步用于非并行GC: 22. 调用函数dvmResumeAllThreads唤醒第1步挂起的线程。 第23步用到并行和非并行GC: 23. 调用函数dvmEnqueueClearedReferences将那些目标对象已经被回收了的引用对象增加到相应的Java队列中去,以便应用程序可以知道哪些引用引用的对象已经被回收了。 以上就是并行和非并行GC的执行总体流程,它们的主要区别在于,前者在GC过程中,有条件地挂起和唤醒非GC线程,而后者在执行GC的过程中,一直都是挂起非GC线程的。并行GC通过有条件地挂起和唤醒非GC线程,就可以使得应用程序获得更好的响应性。但是我们也应该看到,并行GC需要多执行一次标记根集对象以及递归标记那些在GC过程被访问了的对象的操作。也就是说,并行GC在使用得应用程序获得更好的响应性的同时,也需要花费更多的CPU资源。 为了更深入地理解GC的执行过程,接下来我们再详细分析在上述的23步中涉及到的重要函数。 1. dvmSuspendAllThreads 函数dvmSuspendAllThreads用来挂起Dalvik虚拟机中除当前线程之外的其它线程,它的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void dvmSuspendAllThreads(SuspendCause why) Thread* self = dvmThreadSelf(); Thread* thread; . lockThreadSuspend(susp-all, why); . dvmLockThreadList(self); lockThreadSuspendCount(); for (thread = gDvm.threadList; thread != NULL; thread = thread-next) if (thread = self) continue; /* debugger events dont suspend JDWP thread */ if (why = SUSPEND_FOR_DEBUG | why = SUSPEND_FOR_DEBUG_EVENT) & thread-handle = dvmJdwpGetDebugThread(gDvm.jdwpState) continue; dvmAddToSuspendCounts(thread, 1, (why = SUSPEND_FOR_DEBUG | why = SUSPEND_FOR_DEBUG_EVENT) ? 1 : 0); unlockThreadSuspendCount(); for (thread = gDvm.threadList; thread != NULL; thread = thread-next) if (thread = self) continue; /* debugger events dont suspend JDWP thread */ if (why = SUSPEND_FOR_DEBUG | why = SUSPEND_FOR_DEBUG_EVENT) & thread-handle = dvmJdwpGetDebugThread(gDvm.jdwpState) continue; /* wait for the other thread to see the pending suspend */ waitForThreadSuspend(self, thread); . dvmUnlockThreadList(); unlockThreadSuspend(); . 这个函数定义在文件dalvik/vm/Thread.cpp中。 在以下三种情况下,当前线程需要挂起其它线程: 1. 执行GC。 2. 调试器请求。 3. 发生调试事件,如断点命中。 而且,上述三种情况有可能是同时发生的。例如,一个线程在分配对象的过程中发生GC的同时,调试器也刚好请求挂起所有线程。这时候就需要保证每一个挂起操作都是顺序执行的,即要对挂起线程的操作进行加锁。这是通过调用函数函数lockThreadSuspend来实现的。 函数lockThreadSuspend尝试获取gDvm._threadSuspendLock锁。如果获取失败,就表明有其它线程也正在请求挂起Dalvik虚拟机中的线程,包括当前线程。每一个Dalvik虚拟机线程都有一个Suspend Count计数,每当它们都挂起的时候,对应的Suspend Count计数就会加1,而当被唤醒时,对应的Suspend Count计数就会减1。在获取gDvm._threadSuspendLock锁失败的情况下,当前线程按照一定的时间间隔检查自己的Suspend Count,直到自己的Suspend Count等于0,并且能成功获取gDvm._threadSuspendLock锁为止。这样就可以保证每一个挂起Dalvik虚拟机线程的请求都能够得到顺序执行。 从函数lockThreadSuspend返回之后,就表明当前线程可以执行挂起其它线程的操作了。它首先要做的第一件事情是遍历Dalvik虚拟机线程列表,并且调用函数dvmAddToSuspendCounts将列表里面的每一个线程对应的Suspend Count都增加1,但是除了当前线程之外。注意,当挂起线程的请求是调试器发出或者是由调试事件触发的时候,Dalvik虚拟机中的JDWP线程是不可以挂起的,因为JDWP线程挂起来之后,就没法调试了。在这种情况下,也不能将JDWP线程对应的Suspend Count都增加1。 通过调用函数dvmJdwpGetDebugThread可以获得JDWP线程句柄,用这个句柄和Dalvik虚拟机线程列表中的线程句柄相比,就可以知道当前遍历的线程是否是JDWP线程。同时,通过参数why可以知道当挂起线程的请求是否是由调试器发出的或者由调试事件触发的, 注意,在增加被挂起线程的Suspend Count计数之前,必须要获取gDvm.threadSuspendCountLock锁。这个锁的获取和释放可以通过函数lockThreadSuspendCount和unlockThreadSuspendCount完成。 将被挂起线程的Suspend Count计数都增加1之后,接下来就是等待被挂起线程自愿将自己挂起来了。这是通过函数waitForThreadSuspend来实现。当一个线程自愿将自己挂起来的时候,会将自己的状态设置为非运行状态(THREAD_RUNNING),这样函数waitForThreadSuspend通过不断地检查一个线程的状态是否处于非运行状态就可以知道它是否已经挂起来了。 那么,一个线程在什么情况才会自愿将自己挂起来呢?一个线程在执行的过程中,会在合适的时候检查自己的Suspend Count计数。一旦该计数值不等于0,那么它就知道有线程请求挂起自己,因此它就会很配合地将自己的状态设置为非运行的,并且将自己挂起来。例如,当一个线程通过解释器执行代码时,就会周期性地检查自己的Suspend Count是否等于0。这里说的周期性,实际上就是碰到IF指令、GOTO指令、SWITCH指令、RETURN指令和THROW指令等时。 2. dvmHeapBeginMarkStep 函数dvmHeapBeginMarkStep用来初始化堆的标记范围和Mark Stack,它的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片bool dvmHeapBeginMarkStep(bool isPartial) GcMarkContext *ctx = &gDvm.gcHeap-markContext; if (!createMarkStack(&ctx-stack) return false; ctx-finger = NULL; ctx-immuneLimit = (char*)dvmHeapSourceGetImmuneLimit(isPartial); return true; 这个函数定义在文件dalvik/vm/alloc/MarkSweep.cpp中。 在标记过程中用到的各种信息都保存一个GcMarkContext结构体描述的GC标记上下文ctx中。其中,ctx-stack描述的是Mark Stack,ctx-finger描述的是一个标记指纹,在递归标记对象时会用到,ctx-immuneLimit用来限定堆的标记范围。 函数dvmHeapBeginMarkStep调用另外一个函数createMarkStack来初始化Mark Stack,它的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片static bool createMarkStack(GcMarkStack *stack) assert(stack != NULL); size_t length = dvmHeapSourceGetIdealFootprint() * sizeof(Object*) / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD); madvise(stack-base, length, MADV_NORMAL); stack-top = stack-base; return true; 这个函数定义在文件dalvik/vm/alloc/MarkSweep.cpp中。 函数createMarkStack根据最坏情况来设置Mark Stack的长度,也就是用当前堆的大小除以对象占用的最小内存得到的结果。当前堆的大小可以通过函数dvmHeapSourceGetIdealFootprint来获得。在堆上分配的对象,都是从Object类继承下来的,因此,每一个对象占用的最小内存是sizeof(Object)。由于在为对象分配内存时,还需要额外的内存来保存管理信息,例如实际分配给对象的内存字节数。这些额外的管理信息占用的内存字节数通过宏HEAP_SOURCE_CHUNK_OVERHEAD来描述。 由于Mark Stack实际上是一个Object指针数组,因此有了上面的信息之后,我们就可以计算出Mark Stack的长度length。Mark Stack使用的内存块在Dalvik虚拟机启动的时候就创建好的,具体参考前面一文,起始地址位于stack-base中。由于这块内存开始的时候被函数madvice设置为MADV_DONTNEED,因此这里要将它重新设置为MADV_NORMAL,以便可以通知内核,这块内存要开始使用了。 Mark Stack的栈顶stack-top指向的是Mark Stack内存块的起始位置,以后就会从这个位置开始由小到大增长。 回到函数dvmHeapBeginMarkStep中,ctx-immuneLimit记录的实际上是堆的起始标记位置。在此位置之前的对象,都不会被GC。这个位置是通过函数dvmHeapSourceGetImmuneLimit来确定的,它的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void *dvmHeapSourceGetImmuneLimit(bool isPartial) if (isPartial) return hs2heap(gHs)-base; else return NULL; 这个函数定义在文件dalvik/vm/alloc/HeapSource.cpp中。 当参数isPartial等于true时,函数dvmHeapSourceGetImmuneLimit返回的是Active堆的起始位置,否则的话就返回NULL值。也就是说,如果当前执行的GC只要求回收部分垃圾,那么就只回收Active堆的垃圾,否则的话,就同时回收Active堆和Zygote堆的垃圾。 3. dvmHeapMarkRootSet 函数dvmHeapMarkRootSet用来的标记根集对象,它的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片/* Mark the set of root objects. * * Things we need to scan: * - System classes defined by root classloader * - For each thread: * - Interpreted stack, from top to curFrame * - Dalvik registers (args + local vars) * - JNI local references * - Automatic VM local references (TrackedAlloc)
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 电极制造工客户流失预防考核试卷及答案
- 合作达成协议书
- 成列协议书底板
- 公司钢丝绳制造工岗位合规化技术规程
- 化学铣切工岗位合规化技术规程
- 2025型材产品购销合同
- 新疆乌鲁木齐市沙依巴克区2026届数学七年级第一学期期末监测试题含解析
- 2025年北京市房屋租赁合同范本个人使用版
- 专项知识培训简报课件
- 专项培训知识课件
- 生物安全实验室管理体系文件
- 【政史地 江苏卷】2025年江苏省高考招生统一考试高考真题政治+历史+地理试卷(真题+答案)
- 物业工程部库房管理规定
- 奶茶店消防应急预案
- 散文集《皮囊》读书分享PPT模板下载
- GB/T 17554.1-2006识别卡测试方法第1部分:一般特性测试
- GB 17790-2008家用和类似用途空调器安装规范
- 《测控技术与仪器专业导论》配套教学课件
- 喷砂检验报告
- 小学家长会校长发言课件
- QC小组活动记录完整Word版
评论
0/150
提交评论