void dvmCollectGarbageInternal(const GcSpec* spec)
{
GcHeap *gcHeap = gDvm.gcHeap;
u4 gcEnd = 0;
u4 rootStart = 0 , rootEnd = 0;
u4 dirtyStart = 0, dirtyEnd = 0;
size_t numObjectsFreed, numBytesFreed;
size_t currAllocated, currFootprint;
size_t percentFree;
int oldThreadPriority = INT_MAX;
/* The heap lock must be held.
*/
if (gcHeap->gcRunning) {
LOGW_HEAP("Attempted recursive GC");
return;
}
// Trace the beginning of the top-level GC.
if (spec == GC_FOR_MALLOC) {
ATRACE_BEGIN("GC (alloc)");
} else if (spec == GC_CONCURRENT) {
ATRACE_BEGIN("GC (concurrent)");
} else if (spec == GC_EXPLICIT) {
ATRACE_BEGIN("GC (explicit)");
} else if (spec == GC_BEFORE_OOM) {
ATRACE_BEGIN("GC (before OOM)");
} else {
ATRACE_BEGIN("GC (unknown)");
}
gcHeap->gcRunning = true;
rootStart = dvmGetRelativeTimeMsec();
ATRACE_BEGIN("GC: Threads Suspended"); // Suspend A
dvmSuspendAllThreads(SUSPEND_FOR_GC);
/*
* If we are not marking concurrently raise the priority of the
* thread performing the garbage collection.
*/
if (!spec->isConcurrent) {
oldThreadPriority = os_raiseThreadPriority();
}
if (gDvm.preVerify) {
LOGV_HEAP("Verifying roots and heap before GC");
verifyRootsAndHeap();
}
dvmMethodTraceGCBegin();
/* Set up the marking context.
*/
if (!dvmHeapBeginMarkStep(spec->isPartial)) {
ATRACE_END(); // Suspend A
ATRACE_END(); // Top-level GC
LOGE_HEAP("dvmHeapBeginMarkStep failed; aborting");
dvmAbort();
}
/* Mark the set of objects that are strongly reachable from the roots.
*/
LOGD_HEAP("Marking...");
//设置根节点,强可达类型
dvmHeapMarkRootSet();
/* dvmHeapScanMarkedObjects() will build the lists of known
* instances of the Reference classes.
*/
assert(gcHeap->softReferences == NULL);
assert(gcHeap->weakReferences == NULL);
assert(gcHeap->finalizerReferences == NULL);
assert(gcHeap->phantomReferences == NULL);
assert(gcHeap->clearedReferences == NULL);
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);
ATRACE_END(); // Suspend A
rootEnd = dvmGetRelativeTimeMsec();
}
/* Recursively mark any objects that marked objects point to strongly.
* If we're not collecting soft references, soft-reachable
* objects will also be marked.
*/
LOGD_HEAP("Recursing...");
//递归所有对象
dvmHeapScanMarkedObjects();
if (spec->isConcurrent) {
/*
* Re-acquire the heap lock and perform the final thread
* suspension.
*/
dirtyStart = dvmGetRelativeTimeMsec();
dvmLockHeap();
ATRACE_BEGIN("GC: Threads Suspended"); // Suspend B
dvmSuspendAllThreads(SUSPEND_FOR_GC);
/*
* As no barrier intercepts root updates, we conservatively
* assume all roots may be gray and re-mark them.
*/
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.
*/
dvmHeapReScanMarkedObjects();
}
/*
* All strongly-reachable objects have now been marked. Process
* weakly-reachable objects discovered while tracing.
*/
dvmHeapProcessReferences(&gcHeap->softReferences,
spec->doPreserve == false,
&gcHeap->weakReferences,
&gcHeap->finalizerReferences,
&gcHeap->phantomReferences);
#if defined(WITH_JIT)
/*
* Patching a chaining cell is very cheap as it only updates 4 words. It's
* the overhead of stopping all threads and synchronizing the I/D cache
* that makes it expensive.
*
* Therefore we batch those work orders in a queue and go through them
* when threads are suspended for GC.
*/
dvmCompilerPerformSafePointChecks();
#endif
LOGD_HEAP("Sweeping...");
dvmHeapSweepSystemWeaks();
/*
* Live objects have a bit set in the mark bitmap, swap the mark
* and live bitmaps. The sweep can proceed concurrently viewing
* the new live bitmap as the old mark bitmap, and vice versa.
*/
dvmHeapSourceSwapBitmaps();
if (gDvm.postVerify) {
LOGV_HEAP("Verifying roots and heap after GC");
verifyRootsAndHeap();
}
if (spec->isConcurrent) {
dvmUnlockHeap();
dvmResumeAllThreads(SUSPEND_FOR_GC);
ATRACE_END(); // Suspend B
dirtyEnd = dvmGetRelativeTimeMsec();
}
dvmHeapSweepUnmarkedObjects(spec->isPartial, spec->isConcurrent,
&numObjectsFreed, &numBytesFreed);
LOGD_HEAP("Cleaning up...");
dvmHeapFinishMarkStep();
if (spec->isConcurrent) {
dvmLockHeap();
}
LOGD_HEAP("Done.");
/* 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.
*/
//调整heap的大小
dvmHeapSourceGrowForUtilization();
currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
dvmMethodTraceGCEnd();
LOGV_HEAP("GC finished");
gcHeap->gcRunning = false;
LOGV_HEAP("Resuming threads");
/*
* Move queue of pending references back into Java.
*/
dvmEnqueueClearedReferences(&gDvm.gcHeap->clearedReferences);
gcEnd = dvmGetRelativeTimeMsec();
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;
#if ENABLE_STAT_GC
// for gc_for_alloc
accumulateGcStat(3, markSweepTime, numBytesFreed);
#endif
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);
}
if (gcHeap->ddmHpifWhen != 0) {
LOGD_HEAP("Sending VM heap info to DDM");
dvmDdmSendHeapInfo(gcHeap->ddmHpifWhen, false);
}
if (gcHeap->ddmHpsgWhen != 0) {
LOGD_HEAP("Dumping VM heap to DDM");
dvmDdmSendHeapSegments(false, false);
}
if (gcHeap->ddmNhsgWhen != 0) {
LOGD_HEAP("Dumping native heap to DDM");
dvmDdmSendHeapSegments(false, true);
}
ATRACE_END(); // Top-level GC
}