Android Dalvik虚拟机 GC流程分析
Posted baiiu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Dalvik虚拟机 GC流程分析相关的知识,希望对你有一定的参考价值。
前言
本篇继续介绍安卓dalvik虚拟机系列,介绍Dalvik虚拟技的GC流程。
GC结构体
- dalvik/vm/alloc/Heap.h
static const GcSpec kGcForMallocSpec =
true, /* isPartial */
false, /* isConcurrent */
true, /* doPreserve */
"GC_FOR_ALLOC"
;
/* Not enough space for an "ordinary" Object to be allocated. */
const GcSpec *GC_FOR_MALLOC = &kGcForMallocSpec;
static const GcSpec kGcConcurrentSpec =
true, /* isPartial */
true, /* isConcurrent */
true, /* doPreserve */
"GC_CONCURRENT"
;
/* Automatic GC triggered by exceeding a heap occupancy threshold. */
const GcSpec *GC_CONCURRENT = &kGcConcurrentSpec;
static const GcSpec kGcExplicitSpec =
false, /* isPartial */
true, /* isConcurrent */
true, /* doPreserve */
"GC_EXPLICIT"
;
/* Explicit GC via Runtime.gc(), VMRuntime.gc(), or SIGUSR1. */
const GcSpec *GC_EXPLICIT = &kGcExplicitSpec;
static const GcSpec kGcBeforeOomSpec =
false, /* isPartial */
false, /* isConcurrent */
false, /* doPreserve */
"GC_BEFORE_OOM"
;
/* Final attempt to reclaim memory before throwing an OOM. */
const GcSpec *GC_BEFORE_OOM = &kGcBeforeOomSpec;
gcDemonThread启动
虚拟机启动时会初始化gcDemonThread,等待被唤醒调用,主要执行concurrent gc。
有两个时机:
- 一是主动调用
dvmSignalCond(&gHs->gcThreadCond);
唤醒锁,此处是在分配对象时超过concurrentStartBytes时调用; - 二是超时唤醒,此时会执行trimHeaps,向系统归还虚拟内存和物理内存。
/*
* The garbage collection daemon. Initiates a concurrent collection
* when signaled. Also periodically trims the heaps when a few seconds
* have elapsed since the last concurrent GC.
*/
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);
if (gDvm.debuggerConnected)
continue;
dvmLockHeap();
/*
* Another thread may have started a concurrent garbage
* collection before we were scheduled. Check for this
* condition before proceeding.
*/
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;
GC流程
调用dvmCollectGarbageInternal方法,进行各种类型的GC过程。
- concurrent gc会dvmSuspendAllThreads两次,但每次耗时短,整体对app运行影响不大,代码中分位了suspend A 和 suspend B。
- malloc gc会一直dvmSuspendAllThreads,是stop the world类型GC,会造成app卡顿。
void dvmCollectGarbageInternal(const GcSpec* spec)
if (gcHeap->gcRunning)
return;
gcHeap->gcRunning = true;
// GC开始时间
rootStart = dvmGetRelativeTimeMsec();
// 挂起除gc以外所有线程
dvmSuspendAllThreads(SUSPEND_FOR_GC); // Suspend A
// If we are not marking concurrently raise the priority of the thread performing the garbage collection. 非并发gc则提高线程优先级
if (!spec->isConcurrent)
oldThreadPriority = os_raiseThreadPriority();
// Verifying roots and heap before GC,检测roots是否有效
if (gDvm.preVerify)
verifyRootsAndHeap();
// 创建GcMarkStack,isPartial为true则只回收heap[0]堆的内存
dvmHeapBeginMarkStep(spec->isPartial);
// Mark the set of objects that are strongly reachable from the roots. 搜集根节点
dvmHeapMarkRootSet();
// 并发gc在这里释放锁,Suspend A阶段完成
if (spec->isConcurrent)
// Resume threads while tracing from the roots. We unlock the heap to allow mutator threads to allocate from free space.
dvmClearCardTable();
dvmUnlockHeap();
dvmResumeAllThreads(SUSPEND_FOR_GC); // Suspend A
rootEnd = dvmGetRelativeTimeMsec(); // 阶段A耗时
// Recursively mark any objects that marked objects point to strongly. If we're not collecting soft references, soft-reachable objects will also be marked.
// 以markbits中标记的root引用开始,采用递归的方法把所有对象的强引用对象都在markbits里标记上,同时将这些对象压入GcMarkStack中
dvmHeapScanMarkedObjects();
// 并发gc再收集一遍,主要是cardTable这里。cardTable:为了记录在垃圾收集过程中对象的引用情况的,以便可以实现Concurrent GC
if (spec->isConcurrent)
// Re-acquire the heap lock and perform the final thread suspension.
dirtyStart = dvmGetRelativeTimeMsec();
dvmLockHeap();
dvmSuspendAllThreads(SUSPEND_FOR_GC); // Suspend B
dvmHeapReMarkRootSet();
// With the exception of reference objects and weak interned strings, all gray objects should now be on dirty cards.
if (gDvm.verifyCardTable)
dvmVerifyCardTable();
// Recursively mark gray objects pointed to by the roots or by heap objects dirtied during the concurrent mark. 这里从cardTable里遍历被标记为dirty的元素
dvmHeapReScanMarkedObjects();
// All strongly-reachable objects have now been marked. Process weakly-reachable objects discovered while tracing. Process reference class instances and schedule finalizations. 收集一些弱引用了;
dvmHeapProcessReferences(&gcHeap->softReferences,
spec->doPreserve == false,
&gcHeap->weakReferences,
&gcHeap->finalizerReferences,
&gcHeap->phantomReferences);
// Process all the internal system structures that behave like weakly-held objects. 收集内部的一些弱引用的变量,如jni的弱引用
dvmHeapSweepSystemWeaks();
// 交换liveBits和markBits,因为现在markBits保存的是GC后的对象而liveBits还是GC以前的,因此直接交换两者,这样就不用再花时间去重建liveBits了
dvmHeapSourceSwapBitmaps();
// 用新的livebits去检查引用是否有效
if (gDvm.postVerify)
verifyRootsAndHeap();
if (spec->isConcurrent)
dvmUnlockHeap();
dvmResumeAllThreads(SUSPEND_FOR_GC);
dirtyEnd = dvmGetRelativeTimeMsec(); // 并发回收阶段Suspend B结束
// Walk through the list of objects that haven't been marked and free them. Assumes the bitmaps have been swapped. 前面收集完成了,clear所有未标注对象。
dvmHeapSweepUnmarkedObjects(spec->isPartial, spec->isConcurrent, &numObjectsFreed, &numBytesFreed);
// 释放markBits 和 GcMarkStack栈
dvmHeapFinishMarkStep();
if (spec->isConcurrent)
dvmLockHeap();
/* Now's a good time to adjust the heap size, since
* we know what our utilization is.
*
* This doesn't actually resize any memory;
* it just lets the heap grow more when necessary.
*/
// 每次gc后,尝试着去调整堆大小,按照已分配内存 / 堆利用率 去调整堆大小
dvmHeapSourceGrowForUtilization();
currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
if (spec->isConcurrent)
// 唤醒所有堆的锁
dvmBroadcastCond(&gDvm.gcHeapCond);
// 同步的回收此处才是Suspend A结束点
if (!spec->isConcurrent)
dvmResumeAllThreads(SUSPEND_FOR_GC);
dirtyEnd = dvmGetRelativeTimeMsec(); // Suspend A
if (oldThreadPriority != INT_MAX)
os_lowerThreadPriority(oldThreadPriority);
// 触发被回收对象的referenceQueue
dvmEnqueueClearedReferences(&gDvm.gcHeap->clearedReferences);
gcEnd = dvmGetRelativeTimeMsec(); // 一次gc运行总耗时,pause为真正suspendAll的耗时
// 打印日志
percentFree = 100 - (size_t)(100.0f * (float)currAllocated / currFootprint);
if (!spec->isConcurrent)
u4 markSweepTime = dirtyEnd - rootStart;
u4 gcTime = gcEnd - rootStart;
bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
ALOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, paused %ums, total %ums",
spec->reason,
isSmall ? "<" : "",
numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
percentFree,
currAllocated / 1024, currFootprint / 1024,
markSweepTime, gcTime);
else
u4 rootTime = rootEnd - rootStart;
u4 dirtyTime = dirtyEnd - dirtyStart;
u4 gcTime = gcEnd - rootStart;
bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
ALOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, paused %ums+%ums, total %ums",
spec->reason,
isSmall ? "<" : "",
numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
percentFree,
currAllocated / 1024, currFootprint / 1024,
rootTime, dirtyTime, gcTime);
- dalvik/vm/alloc/MarkSweep.cpp
/* 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)
* - Associated Thread/VMThread object
* - ThreadGroups (could track & start with these instead of working
* upward from Threads)
* - Exception currently being thrown, if present
* - JNI global references
* - Interned string table
* - Primitive classes
* - Special objects
* - gDvm.outOfMemoryObj
* - Objects in debugger object registry
*
* Don't need:
* - Native stack (for in-progress stuff in the VM)
* - The TrackedAlloc stuff watches all native VM references.
*/
void dvmHeapMarkRootSet()
GcHeap *gcHeap = gDvm.gcHeap;
dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);
dvmVisitRoots(rootMarkObjectVisitor, &gcHeap->markContext);
以上是关于Android Dalvik虚拟机 GC流程分析的主要内容,如果未能解决你的问题,请参考以下文章