/* 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 allocated with ALLOC_NO_GC * - Objects pending finalization (but not yet finalized) * - 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; HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_STICKY_CLASS, 0); LOG_SCAN("immune objects"); dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit); LOG_SCAN("root class loader\n"); dvmGcScanRootClassLoader(); LOG_SCAN("primitive classes\n"); dvmGcScanPrimitiveClasses(); /* dvmGcScanRootThreadGroups() sets a bunch of * different scan states internally. */ HPROF_CLEAR_GC_SCAN_STATE(); LOG_SCAN("root thread groups\n"); dvmGcScanRootThreadGroups(); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_INTERNED_STRING, 0); LOG_SCAN("interned strings\n"); dvmGcScanInternedStrings(); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_GLOBAL, 0); LOG_SCAN("JNI global refs\n"); dvmGcMarkJniGlobalRefs(); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0); LOG_SCAN("pending reference operations\n"); dvmHeapMarkLargeTableRefs(gcHeap->referenceOperations); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0); LOG_SCAN("pending finalizations\n"); dvmHeapMarkLargeTableRefs(gcHeap->pendingFinalizationRefs); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_DEBUGGER, 0); LOG_SCAN("debugger refs\n"); dvmGcMarkDebuggerRefs(); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_VM_INTERNAL, 0); /* Mark any special objects we have sitting around. */ LOG_SCAN("special objects\n"); dvmMarkObjectNonNull(gDvm.outOfMemoryObj); dvmMarkObjectNonNull(gDvm.internalErrorObj); dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj); //TODO: scan object references sitting in gDvm; use pointer begin & end HPROF_CLEAR_GC_SCAN_STATE(); }
static void hprofDumpUnmarkedObjects(const HeapBitmap markBitmaps[], const HeapBitmap objectBitmaps[], size_t numBitmaps) { hprof_context_t *hctx = gDvm.gcHeap->hprofContext; if (hctx == NULL) { return; } LOGI("hprof: dumping unreachable objects\n"); HPROF_SET_GC_SCAN_STATE(HPROF_UNREACHABLE, 0); dvmHeapBitmapXorWalkLists(markBitmaps, objectBitmaps, numBitmaps, hprofUnreachableBitmapCallback, hctx); HPROF_CLEAR_GC_SCAN_STATE(); }
/* All objects for stronger reference levels have been * marked before this is called. */ void dvmHeapHandleReferences(Object *refListHead, enum RefType refType) { Object *reference; GcMarkContext *markContext = &gDvm.gcHeap->markContext; const int offVmData = gDvm.offJavaLangRefReference_vmData; const int offReferent = gDvm.offJavaLangRefReference_referent; bool workRequired = false; size_t numCleared = 0; size_t numEnqueued = 0; reference = refListHead; while (reference != NULL) { Object *next; Object *referent; /* Pull the interesting fields out of the Reference object. */ next = dvmGetFieldObject(reference, offVmData); referent = dvmGetFieldObject(reference, offReferent); //TODO: when handling REF_PHANTOM, unlink any references // that fail this initial if(). We need to re-walk // the list, and it would be nice to avoid the extra // work. if (referent != NULL && !isMarked(ptr2chunk(referent), markContext)) { bool schedClear, schedEnqueue; /* This is the strongest reference that refers to referent. * Do the right thing. */ switch (refType) { case REF_SOFT: case REF_WEAK: schedClear = clearReference(reference); schedEnqueue = enqueueReference(reference); break; case REF_PHANTOM: /* PhantomReferences are not cleared automatically. * Until someone clears it (or the reference itself * is collected), the referent must remain alive. * * It's necessary to fully mark the referent because * it will still be present during the next GC, and * all objects that it points to must be valid. * (The referent will be marked outside of this loop, * after handing all references of this strength, in * case multiple references point to the same object.) */ schedClear = false; /* A PhantomReference is only useful with a * queue, but since it's possible to create one * without a queue, we need to check. */ schedEnqueue = enqueueReference(reference); break; default: assert(!"Bad reference type"); schedClear = false; schedEnqueue = false; break; } numCleared += schedClear ? 1 : 0; numEnqueued += schedEnqueue ? 1 : 0; if (schedClear || schedEnqueue) { uintptr_t workBits; /* Stuff the clear/enqueue bits in the bottom of * the pointer. Assumes that objects are 8-byte * aligned. * * Note that we are adding the *Reference* (which * is by definition already marked at this point) to * this list; we're not adding the referent (which * has already been cleared). */ assert(((intptr_t)reference & 3) == 0); assert(((WORKER_CLEAR | WORKER_ENQUEUE) & ~3) == 0); workBits = (schedClear ? WORKER_CLEAR : 0) | (schedEnqueue ? WORKER_ENQUEUE : 0); if (!dvmHeapAddRefToLargeTable( &gDvm.gcHeap->referenceOperations, (Object *)((uintptr_t)reference | workBits))) { LOGE_HEAP("dvmMalloc(): no room for any more " "reference operations\n"); dvmAbort(); } workRequired = true; } if (refType != REF_PHANTOM) { /* Let later GCs know not to reschedule this reference. */ dvmSetFieldObject(reference, offVmData, SCHEDULED_REFERENCE_MAGIC); } // else this is handled later for REF_PHANTOM } // else there was a stronger reference to the referent. reference = next; } #define refType2str(r) \ ((r) == REF_SOFT ? "soft" : ( \ (r) == REF_WEAK ? "weak" : ( \ (r) == REF_PHANTOM ? "phantom" : "UNKNOWN" ))) LOGD_HEAP("dvmHeapHandleReferences(): cleared %zd, enqueued %zd %s references\n", numCleared, numEnqueued, refType2str(refType)); /* Walk though the reference list again, and mark any non-clear/marked * referents. Only PhantomReferences can have non-clear referents * at this point. */ if (refType == REF_PHANTOM) { bool scanRequired = false; HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0); reference = refListHead; while (reference != NULL) { Object *next; Object *referent; /* Pull the interesting fields out of the Reference object. */ next = dvmGetFieldObject(reference, offVmData); referent = dvmGetFieldObject(reference, offReferent); if (referent != NULL && !isMarked(ptr2chunk(referent), markContext)) { markObjectNonNull(referent, markContext); scanRequired = true; /* Let later GCs know not to reschedule this reference. */ dvmSetFieldObject(reference, offVmData, SCHEDULED_REFERENCE_MAGIC); } reference = next; } HPROF_CLEAR_GC_SCAN_STATE(); if (scanRequired) { processMarkStack(markContext); } } if (workRequired) { dvmSignalHeapWorker(false); } }
/* 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 allocated with ALLOC_NO_GC * - Objects pending finalization (but not yet finalized) * - 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() { HeapRefTable *refs; GcHeap *gcHeap; Object **op; gcHeap = gDvm.gcHeap; HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_STICKY_CLASS, 0); LOG_SCAN("root class loader\n"); dvmGcScanRootClassLoader(); LOG_SCAN("primitive classes\n"); dvmGcScanPrimitiveClasses(); /* dvmGcScanRootThreadGroups() sets a bunch of * different scan states internally. */ HPROF_CLEAR_GC_SCAN_STATE(); LOG_SCAN("root thread groups\n"); dvmGcScanRootThreadGroups(); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_INTERNED_STRING, 0); LOG_SCAN("interned strings\n"); dvmGcScanInternedStrings(); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_JNI_GLOBAL, 0); LOG_SCAN("JNI global refs\n"); dvmGcMarkJniGlobalRefs(); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0); LOG_SCAN("pending reference operations\n"); dvmHeapMarkLargeTableRefs(gcHeap->referenceOperations, true); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0); LOG_SCAN("pending finalizations\n"); dvmHeapMarkLargeTableRefs(gcHeap->pendingFinalizationRefs, false); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_DEBUGGER, 0); LOG_SCAN("debugger refs\n"); dvmGcMarkDebuggerRefs(); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_VM_INTERNAL, 0); /* Mark all ALLOC_NO_GC objects. */ LOG_SCAN("ALLOC_NO_GC objects\n"); refs = &gcHeap->nonCollectableRefs; op = refs->table; while ((uintptr_t)op < (uintptr_t)refs->nextEntry) { dvmMarkObjectNonNull(*(op++)); } /* Mark any special objects we have sitting around. */ LOG_SCAN("special objects\n"); dvmMarkObjectNonNull(gDvm.outOfMemoryObj); dvmMarkObjectNonNull(gDvm.internalErrorObj); //TODO: scan object references sitting in gDvm; use pointer begin & end HPROF_CLEAR_GC_SCAN_STATE(); }
/* Find unreachable objects that need to be finalized, * and schedule them for finalization. */ void dvmHeapScheduleFinalizations() { HeapRefTable newPendingRefs; LargeHeapRefTable *finRefs = gDvm.gcHeap->finalizableRefs; Object **ref; Object **lastRef; size_t totalPendCount; GcMarkContext *markContext = &gDvm.gcHeap->markContext; /* * All reachable objects have been marked. * Any unmarked finalizable objects need to be finalized. */ /* Create a table that the new pending refs will * be added to. */ if (!dvmHeapInitHeapRefTable(&newPendingRefs, 128)) { //TODO: mark all finalizable refs and hope that // we can schedule them next time. Watch out, // because we may be expecting to free up space // by calling finalizers. LOGE_GC("dvmHeapScheduleFinalizations(): no room for " "pending finalizations\n"); dvmAbort(); } /* Walk through finalizableRefs and move any unmarked references * to the list of new pending refs. */ totalPendCount = 0; while (finRefs != NULL) { Object **gapRef; size_t newPendCount = 0; gapRef = ref = finRefs->refs.table; lastRef = finRefs->refs.nextEntry; while (ref < lastRef) { DvmHeapChunk *hc; hc = ptr2chunk(*ref); if (!isMarked(hc, markContext)) { if (!dvmHeapAddToHeapRefTable(&newPendingRefs, *ref)) { //TODO: add the current table and allocate // a new, smaller one. LOGE_GC("dvmHeapScheduleFinalizations(): " "no room for any more pending finalizations: %zd\n", dvmHeapNumHeapRefTableEntries(&newPendingRefs)); dvmAbort(); } newPendCount++; } else { /* This ref is marked, so will remain on finalizableRefs. */ if (newPendCount > 0) { /* Copy it up to fill the holes. */ *gapRef++ = *ref; } else { /* No holes yet; don't bother copying. */ gapRef++; } } ref++; } finRefs->refs.nextEntry = gapRef; //TODO: if the table is empty when we're done, free it. totalPendCount += newPendCount; finRefs = finRefs->next; } LOGD_GC("dvmHeapScheduleFinalizations(): %zd finalizers triggered.\n", totalPendCount); if (totalPendCount == 0) { /* No objects required finalization. * Free the empty temporary table. */ dvmClearReferenceTable(&newPendingRefs); return; } /* Add the new pending refs to the main list. */ if (!dvmHeapAddTableToLargeTable(&gDvm.gcHeap->pendingFinalizationRefs, &newPendingRefs)) { LOGE_GC("dvmHeapScheduleFinalizations(): can't insert new " "pending finalizations\n"); dvmAbort(); } //TODO: try compacting the main list with a memcpy loop /* Mark the refs we just moved; we don't want them or their * children to get swept yet. */ ref = newPendingRefs.table; lastRef = newPendingRefs.nextEntry; assert(ref < lastRef); HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0); while (ref < lastRef) { markObjectNonNull(*ref, markContext); ref++; } HPROF_CLEAR_GC_SCAN_STATE(); /* Set markAllReferents so that we don't collect referents whose * only references are in final-reachable objects. * TODO: eventually provide normal reference behavior by properly * marking these references. */ gDvm.gcHeap->markAllReferents = true; processMarkStack(markContext); gDvm.gcHeap->markAllReferents = false; dvmSignalHeapWorker(false); }