GC結構體
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);
}
}