OSDN Git Service

Reference class handling overhaul.
authorCarl Shapiro <cshapiro@google.com>
Fri, 26 Mar 2010 22:34:39 +0000 (15:34 -0700)
committerCarl Shapiro <cshapiro@google.com>
Tue, 6 Apr 2010 04:00:26 +0000 (21:00 -0700)
* Do not mark through soft-reachable referents when scanning f-
  reachable objects.

* Make the SoftReference clearing policy local to better conform to
  developer expecataions.

* Eliminate SCHEDULED_REFERENCE_MAGIC, a hold-over from a previous
  reference management algorithm.

* Move soft, weak and phantom reference processing into their own
  subroutines.

Change-Id: I992788d58f10fa08336e6ac3f0a4dbfa3fc2086f

vm/Globals.h
vm/Jni.c
vm/alloc/Heap.c
vm/alloc/Heap.h
vm/alloc/HeapInternal.h
vm/alloc/HeapTable.c
vm/alloc/HeapWorker.c
vm/alloc/MarkSweep.c
vm/alloc/MarkSweep.h

index 07bf522..551ca1f 100644 (file)
@@ -425,9 +425,6 @@ struct DvmGlobals {
     ReferenceTable  jniPinRefTable;
     pthread_mutex_t jniPinRefLock;
 
-    /* special ReferenceQueue for JNI weak globals */
-    Object*     jniWeakGlobalRefQueue;
-
     /*
      * Native shared library table.
      */
index 6692f3b..15b0294 100644 (file)
--- a/vm/Jni.c
+++ b/vm/Jni.c
@@ -918,53 +918,6 @@ bail:
     dvmUnlockMutex(&gDvm.jniGlobalRefLock);
 }
 
-
-/*
- * Get the "magic" JNI weak global ReferenceQueue.  It's allocated on
- * first use.
- *
- * Returns NULL with an exception raised if allocation fails.
- */
-static Object* getWeakGlobalRefQueue(void)
-{
-    /* use an indirect variable to avoid "type-punned pointer" complaints */
-    Object** pGlobalQ = &gDvm.jniWeakGlobalRefQueue;
-
-    if (*pGlobalQ != NULL)
-        return *pGlobalQ;
-
-    ClassObject* clazz = dvmFindSystemClass("Ljava/lang/ref/ReferenceQueue;");
-    if (clazz == NULL) {
-        LOGE("Unable to find java.lang.ref.ReferenceQueue");
-        dvmAbort();
-    }
-
-    /*
-     * Create an instance of ReferenceQueue.  The object is never actually
-     * used for anything, so we don't need to call a constructor.  (We could
-     * get away with using an instance of Object, but this is cleaner.)
-     */
-    Object* queue = dvmAllocObject(clazz, ALLOC_DEFAULT);
-    if (queue == NULL) {
-        LOGW("Failed allocating weak global ref queue\n");
-        assert(dvmCheckException(dvmThreadSelf()));
-        return NULL;
-    }
-    dvmReleaseTrackedAlloc(queue, NULL);
-
-    /*
-     * Save it, using atomic ops to ensure we don't double-up.  The gDvm
-     * field is known to the GC.
-     */
-    if (!ATOMIC_CMP_SWAP((int*) pGlobalQ, 0, (int) queue)) {
-        LOGD("WOW: lost race to create weak global ref queue\n");
-        queue = *pGlobalQ;
-    }
-
-    return queue;
-}
-
-
 /*
  * We create a PhantomReference that references the object, add a
  * global reference to it, and then flip some bits before returning it.
@@ -980,7 +933,6 @@ static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj)
 
     Thread* self = ((JNIEnvExt*)env)->self;
     Object* obj = dvmDecodeIndirectRef(env, jobj);
-    Object* weakGlobalQueue = getWeakGlobalRefQueue();
     Object* phantomObj;
     jobject phantomRef;
 
@@ -1004,7 +956,7 @@ static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj)
 
     JValue unused;
     dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj,
-        &unused, jobj, weakGlobalQueue);
+        &unused, jobj, NULL);
     dvmReleaseTrackedAlloc(phantomObj, self);
 
     if (dvmCheckException(self)) {
index ed4e4f3..f4d8e7b 100644 (file)
@@ -67,8 +67,6 @@ bool dvmHeapStartup()
     gcHeap->heapWorkerCurrentObject = NULL;
     gcHeap->heapWorkerCurrentMethod = NULL;
     gcHeap->heapWorkerInterpStartTime = 0LL;
-    gcHeap->softReferenceCollectionState = SR_COLLECT_NONE;
-    gcHeap->softReferenceHeapSizeThreshold = gDvm.heapSizeStart;
     gcHeap->ddmHpifWhen = 0;
     gcHeap->ddmHpsgWhen = 0;
     gcHeap->ddmHpsgWhat = 0;
@@ -260,50 +258,13 @@ Object *dvmGetNextHeapWorkerObject(HeapWorkerOperation *op)
     return obj;
 }
 
-/* Used for a heap size change hysteresis to avoid collecting
- * SoftReferences when the heap only grows by a small amount.
- */
-#define SOFT_REFERENCE_GROWTH_SLACK (128 * 1024)
-
 /* Whenever the effective heap size may have changed,
  * this function must be called.
  */
 void dvmHeapSizeChanged()
 {
-    GcHeap *gcHeap = gDvm.gcHeap;
-    size_t currentHeapSize;
-
-    currentHeapSize = dvmHeapSourceGetIdealFootprint();
-
-    /* See if the heap size has changed enough that we should care
-     * about it.
-     */
-    if (currentHeapSize <= gcHeap->softReferenceHeapSizeThreshold -
-            4 * SOFT_REFERENCE_GROWTH_SLACK)
-    {
-        /* The heap has shrunk enough that we'll use this as a new
-         * threshold.  Since we're doing better on space, there's
-         * no need to collect any SoftReferences.
-         *
-         * This is 4x the growth hysteresis because we don't want
-         * to snap down so easily after a shrink.  If we just cleared
-         * up a bunch of SoftReferences, we don't want to disallow
-         * any new ones from being created.
-         * TODO: determine if the 4x is important, needed, or even good
-         */
-        gcHeap->softReferenceHeapSizeThreshold = currentHeapSize;
-        gcHeap->softReferenceCollectionState = SR_COLLECT_NONE;
-    } else if (currentHeapSize >= gcHeap->softReferenceHeapSizeThreshold +
-            SOFT_REFERENCE_GROWTH_SLACK)
-    {
-        /* The heap has grown enough to warrant collecting SoftReferences.
-         */
-        gcHeap->softReferenceHeapSizeThreshold = currentHeapSize;
-        gcHeap->softReferenceCollectionState = SR_COLLECT_SOME;
-    }
 }
 
-
 /* Do a full garbage collection, which may grow the
  * heap as a side-effect if the live set is large.
  */
@@ -737,13 +698,9 @@ void dvmHeapSuspendAndVerify()
  * way to enforce this is to refuse to GC on an allocation made by the
  * JDWP thread -- we have to expand the heap or fail.
  */
-void dvmCollectGarbageInternal(bool collectSoftReferences, GcReason reason)
+void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason)
 {
     GcHeap *gcHeap = gDvm.gcHeap;
-    Object *softReferences;
-    Object *weakReferences;
-    Object *phantomReferences;
-
     u8 now;
     s8 timeSinceLastGc;
     s8 gcElapsedTime;
@@ -760,8 +717,6 @@ void dvmCollectGarbageInternal(bool collectSoftReferences, GcReason reason)
     size_t strongMarkSize = 0;
     size_t finalizeMarkCount = 0;
     size_t finalizeMarkSize = 0;
-    size_t phantomMarkCount = 0;
-    size_t phantomMarkSize = 0;
 #endif
 
     /* The heap lock must be held.
@@ -914,18 +869,6 @@ void dvmCollectGarbageInternal(bool collectSoftReferences, GcReason reason)
     gcHeap->weakReferences = NULL;
     gcHeap->phantomReferences = NULL;
 
-    /* Make sure that we don't hard-mark the referents of Reference
-     * objects by default.
-     */
-    gcHeap->markAllReferents = false;
-
-    /* Don't mark SoftReferences if our caller wants us to collect them.
-     * This has to be set before calling dvmHeapScanMarkedObjects().
-     */
-    if (collectSoftReferences) {
-        gcHeap->softReferenceCollectionState = SR_COLLECT_ALL;
-    }
-
     /* Recursively mark any objects that marked objects point to strongly.
      * If we're not collecting soft references, soft-reachable
      * objects will also be marked.
@@ -939,29 +882,16 @@ void dvmCollectGarbageInternal(bool collectSoftReferences, GcReason reason)
     gcHeap->markSize = 0;
 #endif
 
-    /* Latch these so that the other calls to dvmHeapScanMarkedObjects() don't
-     * mess with them.
-     */
-    softReferences = gcHeap->softReferences;
-    weakReferences = gcHeap->weakReferences;
-    phantomReferences = gcHeap->phantomReferences;
-
     /* All strongly-reachable objects have now been marked.
      */
-    if (gcHeap->softReferenceCollectionState != SR_COLLECT_NONE) {
-        LOGD_HEAP("Handling soft references...");
-        dvmHeapHandleReferences(softReferences, REF_SOFT);
-        // markCount always zero
-
-        /* Now that we've tried collecting SoftReferences,
-         * fall back to not collecting them.  If the heap
-         * grows, we will start collecting again.
-         */
-        gcHeap->softReferenceCollectionState = SR_COLLECT_NONE;
-    } // else dvmHeapScanMarkedObjects() already marked the soft-reachable set
+    LOGD_HEAP("Handling soft references...");
+    if (!clearSoftRefs) {
+        dvmHandleSoftRefs(&gcHeap->softReferences);
+    }
+    dvmClearWhiteRefs(&gcHeap->softReferences);
+
     LOGD_HEAP("Handling weak references...");
-    dvmHeapHandleReferences(weakReferences, REF_WEAK);
-    // markCount always zero
+    dvmClearWhiteRefs(&gcHeap->weakReferences);
 
     /* Once all weak-reachable objects have been taken
      * care of, any remaining unmarked objects can be finalized.
@@ -975,24 +905,22 @@ void dvmCollectGarbageInternal(bool collectSoftReferences, GcReason reason)
     gcHeap->markSize = 0;
 #endif
 
+    LOGD_HEAP("Handling f-reachable soft references...");
+    dvmClearWhiteRefs(&gcHeap->softReferences);
+
+    LOGD_HEAP("Handling f-reachable weak references...");
+    dvmClearWhiteRefs(&gcHeap->weakReferences);
+
     /* Any remaining objects that are not pending finalization
      * could be phantom-reachable.  This will mark any phantom-reachable
      * objects, as well as enqueue their references.
      */
     LOGD_HEAP("Handling phantom references...");
-    dvmHeapHandleReferences(phantomReferences, REF_PHANTOM);
-#if DVM_TRACK_HEAP_MARKING
-    phantomMarkCount = gcHeap->markCount;
-    phantomMarkSize = gcHeap->markSize;
-    gcHeap->markCount = 0;
-    gcHeap->markSize = 0;
-#endif
-
-//TODO: take care of JNI weak global references
+    dvmClearWhiteRefs(&gcHeap->phantomReferences);
 
 #if DVM_TRACK_HEAP_MARKING
-    LOGI_HEAP("Marked objects: %dB strong, %dB final, %dB phantom\n",
-            strongMarkSize, finalizeMarkSize, phantomMarkSize);
+    LOGI_HEAP("Marked objects: %dB strong, %dB final\n",
+              strongMarkSize, finalizeMarkSize);
 #endif
 
 #ifdef WITH_DEADLOCK_PREDICTION
index 5237a56..0b0bc47 100644 (file)
@@ -79,6 +79,6 @@ void dvmHeapSuspendAndVerify();
 /*
  * Run the garbage collector without doing any locking.
  */
-void dvmCollectGarbageInternal(bool collectSoftReferences, GcReason reason);
+void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason);
 
 #endif  // _DALVIK_ALLOC_HEAP
index 91d5976..f078fdd 100644 (file)
@@ -24,8 +24,6 @@
 #include "HeapTable.h"
 #include "MarkSweep.h"
 
-#define SCHEDULED_REFERENCE_MAGIC   ((Object*)0x87654321)
-
 struct GcHeap {
     HeapSource      *heapSource;
 
@@ -111,31 +109,6 @@ struct GcHeap {
      */
     bool            gcRunning;
 
-    /* Set at the end of a GC to indicate the collection policy
-     * for SoftReferences during the following GC.
-     */
-    enum { SR_COLLECT_NONE, SR_COLLECT_SOME, SR_COLLECT_ALL }
-                    softReferenceCollectionState;
-
-    /* The size of the heap is compared against this value
-     * to determine when to start collecting SoftReferences.
-     */
-    size_t          softReferenceHeapSizeThreshold;
-
-    /* A value that will increment every time we see a SoftReference
-     * whose referent isn't marked (during SR_COLLECT_SOME).
-     * The absolute value is meaningless, and does not need to
-     * be reset or initialized at any point.
-     */
-    int             softReferenceColor;
-
-    /* Indicates whether or not the object scanner should bother
-     * keeping track of any references.  If markAllReferents is
-     * true, referents will be hard-marked.  If false, normal
-     * reference following is used.
-     */
-    bool            markAllReferents;
-
 #if DVM_TRACK_HEAP_MARKING
     /* Every time an unmarked object becomes marked, markCount
      * is incremented and markSize increases by the size of
index 61a88ec..7d42600 100644 (file)
@@ -202,10 +202,8 @@ void dvmHeapMarkLargeTableRefs(LargeHeapRefTable *table, bool stripLowBits)
         lastRef = table->refs.nextEntry;
         if (stripLowBits) {
             /* This case is used for marking reference objects that
-             * are still waiting for the heap worker thread to get to
-             * them.  The referents pointed to by the references are
-             * marked when a SCHEDULED_REFERENCE_MAGIC is encountered
-             * during scanning.
+             * are still waiting for the heap worker thread to push
+             * them onto their reference queue.
              */
             while (ref < lastRef) {
                 dvmMarkObjectNonNull((Object *)((uintptr_t)*ref++ & ~3));
index c769521..bf924cd 100644 (file)
@@ -278,6 +278,10 @@ static void doHeapWork(Thread *self)
             callMethod(self, obj, method);
         } else {
             if (op & WORKER_ENQUEUE) {
+                assert(dvmGetFieldObject(
+                           obj, gDvm.offJavaLangRefReference_queue) != NULL);
+                assert(dvmGetFieldObject(
+                           obj, gDvm.offJavaLangRefReference_queueNext) == NULL);
                 numReferencesEnqueued++;
                 callMethod(self, obj,
                         gDvm.methJavaLangRefReference_enqueueInternal);
index 1b62cf4..867636a 100644 (file)
@@ -329,7 +329,6 @@ void dvmHeapMarkRootSet()
     dvmMarkObjectNonNull(gDvm.outOfMemoryObj);
     dvmMarkObjectNonNull(gDvm.internalErrorObj);
     dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj);
-    dvmMarkObject(gDvm.jniWeakGlobalRefQueue);
 //TODO: scan object references sitting in gDvm;  use pointer begin & end
 
     HPROF_CLEAR_GC_SCAN_STATE();
@@ -538,43 +537,10 @@ static void scanObject(const Object *obj, GcMarkContext *ctx)
              */
             referent = dvmGetFieldObject(obj,
                     gDvm.offJavaLangRefReference_referent);
-            if (referent != NULL &&
-                    !isMarked(referent, &gcHeap->markContext))
+            if (referent != NULL && !isMarked(referent, ctx))
             {
                 u4 refFlags;
 
-                if (gcHeap->markAllReferents) {
-                    LOG_REF("Hard-marking a reference\n");
-
-                    /* Don't bother with normal reference-following
-                     * behavior, just mark the referent.  This should
-                     * only be used when following objects that just
-                     * became scheduled for finalization.
-                     */
-                    markObjectNonNull(referent, ctx);
-                    goto skip_reference;
-                }
-
-                /* See if this reference was handled by a previous GC.
-                 */
-                if (dvmGetFieldObject(obj,
-                            gDvm.offJavaLangRefReference_vmData) ==
-                        SCHEDULED_REFERENCE_MAGIC)
-                {
-                    LOG_REF("Skipping scheduled reference\n");
-
-                    /* Don't reschedule it, but make sure that its
-                     * referent doesn't get collected (in case it's
-                     * a PhantomReference and wasn't cleared automatically).
-                     */
-                    //TODO: Mark these after handling all new refs of
-                    //      this strength, in case the new refs refer
-                    //      to the same referent.  Not a very common
-                    //      case, though.
-                    markObjectNonNull(referent, ctx);
-                    goto skip_reference;
-                }
-
                 /* Find out what kind of reference is pointing
                  * to referent.
                  */
@@ -606,25 +572,7 @@ static void scanObject(const Object *obj, GcMarkContext *ctx)
                      * we'll attempt to collect all of them, some of
                      * them, or none of them.
                      */
-                    if (gcHeap->softReferenceCollectionState ==
-                            SR_COLLECT_NONE)
-                    {
-                sr_collect_none:
-                        markObjectNonNull(referent, ctx);
-                    } else if (gcHeap->softReferenceCollectionState ==
-                            SR_COLLECT_ALL)
-                    {
-                sr_collect_all:
-                        ADD_REF_TO_LIST(gcHeap->softReferences, obj);
-                    } else {
-                        /* We'll only try to collect half of the
-                         * referents.
-                         */
-                        if (gcHeap->softReferenceColor++ & 1) {
-                            goto sr_collect_none;
-                        }
-                        goto sr_collect_all;
-                    }
+                    ADD_REF_TO_LIST(gcHeap->softReferences, obj);
                 } else {
                     /* It's a weak or phantom reference.
                      * Clearing CLASS_ISREFERENCE will reveal which.
@@ -642,7 +590,6 @@ static void scanObject(const Object *obj, GcMarkContext *ctx)
             }
         }
 
-    skip_reference:
         /* If this is a class object, mark various other things that
          * its internals point to.
          *
@@ -697,7 +644,7 @@ scanBitmapCallback(size_t numPtrs, void **ptrs, const void *finger, void *arg)
  * reachable objects.  When this returns, the entire set of
  * live objects will be marked and the mark stack will be empty.
  */
-void dvmHeapScanMarkedObjects()
+void dvmHeapScanMarkedObjects(void)
 {
     GcMarkContext *ctx = &gDvm.gcHeap->markContext;
 
@@ -735,9 +682,11 @@ static void clearReference(Object *reference)
             gDvm.offJavaLangRefReference_referent, NULL);
 }
 
-/** @return true if we need to schedule a call to enqueue().
+/*
+ * Returns true if the reference was registered with a reference queue
+ * and has not yet been enqueued.
  */
-static bool enqueueReference(Object *reference)
+static bool isEnqueuable(const Object *reference)
 {
     Object *queue = dvmGetFieldObject(reference,
             gDvm.offJavaLangRefReference_queue);
@@ -757,162 +706,125 @@ static bool enqueueReference(Object *reference)
     return true;
 }
 
-/* All objects for stronger reference levels have been
- * marked before this is called.
+/*
+ * Schedules a reference to be appended to its reference queue.
  */
-void dvmHeapHandleReferences(Object *refListHead, enum RefType refType)
+static void enqueueReference(Object *ref)
 {
-    Object *reference;
-    GcMarkContext *markContext = &gDvm.gcHeap->markContext;
-    const int offVmData = gDvm.offJavaLangRefReference_vmData;
-    const int offReferent = gDvm.offJavaLangRefReference_referent;
-    bool workRequired = false;
-
-    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(referent, markContext)) {
-            bool schedEnqueue;
-
-            /* This is the strongest reference that refers to referent.
-             * Do the right thing.
-             */
-            switch (refType) {
-            case REF_SOFT:
-            case REF_WEAK:
-                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.)
-                 *
-                 * One exception: JNI "weak global" references are handled
-                 * as a special case.  They're identified by the queue.
-                 */
-                if (gDvm.jniWeakGlobalRefQueue != NULL) {
-                    Object* queue = dvmGetFieldObject(reference,
-                            gDvm.offJavaLangRefReference_queue);
-                    if (queue == gDvm.jniWeakGlobalRefQueue) {
-                        LOGV("+++ WGR: clearing + not queueing %p:%p\n",
-                            reference, referent);
-                        clearReference(reference);
-                        schedEnqueue = false;
-                        break;
-                    }
-                }
+    LargeHeapRefTable **table;
+    Object *op;
+
+    assert(((uintptr_t)ref & 3) == 0);
+    assert((WORKER_ENQUEUE & ~3) == 0);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
+    assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
+    /* Stuff the enqueue bit 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).
+     */
+    table = &gDvm.gcHeap->referenceOperations;
+    op = (Object *)((uintptr_t)ref | WORKER_ENQUEUE);
+    if (!dvmHeapAddRefToLargeTable(table, op)) {
+        LOGE_HEAP("enqueueReference(): no room for any more "
+                  "reference operations\n");
+        dvmAbort();
+    }
+}
 
-                /* 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");
-                schedEnqueue = false;
-                break;
-            }
+/*
+ * Walks the reference list marking any references subject to the
+ * reference clearing policy.  References with a black referent are
+ * removed from the list.  References with white referents biased
+ * toward saving are blackened and also removed from the list.
+ */
+void dvmHandleSoftRefs(Object **list)
+{
+    GcMarkContext *markContext;
+    Object *ref, *referent;
+    Object *prev, *next;
+    size_t referentOffset, vmDataOffset;
+    unsigned counter;
+    bool marked;
 
-            if (schedEnqueue) {
-                /* Stuff the enqueue bit 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_ENQUEUE & ~3) == 0);
-                if (!dvmHeapAddRefToLargeTable(
-                        &gDvm.gcHeap->referenceOperations,
-                        (Object *)((uintptr_t)reference | WORKER_ENQUEUE)))
-                {
-                    LOGE_HEAP("dvmMalloc(): no room for any more "
-                            "reference operations\n");
-                    dvmAbort();
-                }
-                workRequired = true;
+    markContext = &gDvm.gcHeap->markContext;
+    vmDataOffset = gDvm.offJavaLangRefReference_vmData;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    counter = 0;
+    prev = next = NULL;
+    ref = *list;
+    while (ref != NULL) {
+        referent = dvmGetFieldObject(ref, referentOffset);
+        next = dvmGetFieldObject(ref, vmDataOffset);
+        assert(referent != NULL);
+        marked = isMarked(referent, markContext);
+        if (!marked && ((++counter) & 1)) {
+            /* Referent is white and biased toward saving, mark it. */
+            markObjectNonNull(referent, markContext);
+            marked = true;
+        }
+        if (marked) {
+            /* Referent is black, unlink it. */
+            if (prev != NULL) {
+                dvmSetFieldObject(ref, vmDataOffset, NULL);
+                dvmSetFieldObject(prev, vmDataOffset, next);
             }
-
-            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;
+        } else {
+            /* Referent is white, skip over it. */
+            prev = ref;
+        }
+        ref = next;
     }
-
-    /* Walk though the reference list again, and mark any non-clear/marked
-     * referents.  Only PhantomReferences can have non-clear referents
-     * at this point.
-     *
-     * (Could skip this for JNI weak globals, since we know they've been
-     * cleared.)
+    /*
+     * Restart the mark with the newly black references added to the
+     * root set.
      */
-    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);
+    processMarkStack(markContext);
+}
 
-            if (referent != NULL && !isMarked(referent, markContext)) {
-                markObjectNonNull(referent, markContext);
-                scanRequired = true;
+/*
+ * Walks the reference list and clears references with an unmarked
+ * (white) referents.  Cleared references registered to a reference
+ * queue are scheduled for appending by the heap worker thread.
+ */
+void dvmClearWhiteRefs(Object **list)
+{
+    GcMarkContext *markContext;
+    Object *ref, *referent;
+    size_t referentOffset, vmDataOffset;
+    bool doSignal;
 
-                /* Let later GCs know not to reschedule this reference.
-                 */
-                dvmSetFieldObject(reference, offVmData,
-                        SCHEDULED_REFERENCE_MAGIC);
+    markContext = &gDvm.gcHeap->markContext;
+    vmDataOffset = gDvm.offJavaLangRefReference_vmData;
+    referentOffset = gDvm.offJavaLangRefReference_referent;
+    doSignal = false;
+    while (*list != NULL) {
+        ref = *list;
+        referent = dvmGetFieldObject(ref, referentOffset);
+        *list = dvmGetFieldObject(ref, vmDataOffset);
+        assert(referent != NULL);
+        if (!isMarked(referent, markContext)) {
+            /* Referent is "white", clear it. */
+            clearReference(ref);
+            if (isEnqueuable(ref)) {
+                enqueueReference(ref);
+                doSignal = true;
             }
-
-            reference = next;
-        }
-        HPROF_CLEAR_GC_SCAN_STATE();
-
-        if (scanRequired) {
-            processMarkStack(markContext);
         }
     }
-
-    if (workRequired) {
+    /*
+     * If we cleared a reference with a reference queue we must notify
+     * the heap worker to append the reference.
+     */
+    if (doSignal) {
         dvmSignalHeapWorker(false);
     }
+    assert(*list == NULL);
 }
 
-
 /* Find unreachable objects that need to be finalized,
  * and schedule them for finalization.
  */
@@ -1018,16 +930,7 @@ void dvmHeapScheduleFinalizations()
         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);
 }
 
index e6d55bc..5323628 100644 (file)
@@ -54,7 +54,8 @@ enum RefType {
 bool dvmHeapBeginMarkStep(GcMode mode);
 void dvmHeapMarkRootSet(void);
 void dvmHeapScanMarkedObjects(void);
-void dvmHeapHandleReferences(Object *refListHead, enum RefType refType);
+void dvmHandleSoftRefs(Object **list);
+void dvmClearWhiteRefs(Object **list);
 void dvmHeapScheduleFinalizations(void);
 void dvmHeapFinishMarkStep(void);