Delegate the queuing of cleared references to managed code.

Previously, the garbage collector would pass each cleared reference to
the heap worker thread for queuing.  The heap worker thread would then
perform a callback into managed code for each cleared reference which
assigned the reference to its reference queue.

With this change, the garbage collector instead links together all of
the cleared references and calls back into managed code exactly once
to hand off the references for processing.  This change makes the heap
worker thread and its data structures obsolete.

Change-Id: I28e02638f0877a7fd2ac96b9c3f5597a38541ebb
diff --git a/vm/Dvm.mk b/vm/Dvm.mk
index c9b0e27..d655a3a 100644
--- a/vm/Dvm.mk
+++ b/vm/Dvm.mk
@@ -120,8 +120,6 @@
 	alloc/CardTable.c \
 	alloc/HeapBitmap.c.arm \
 	alloc/HeapDebug.c \
-	alloc/HeapTable.c \
-	alloc/HeapWorker.c \
 	alloc/Heap.c.arm \
 	alloc/DdmHeap.c \
 	alloc/Verify.c \
diff --git a/vm/Globals.h b/vm/Globals.h
index 1cb4526..faafcc6 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -367,11 +367,14 @@
     int         offJavaLangRefReference_queueNext;
     int         offJavaLangRefReference_pendingNext;
 
-    /* method pointers - java.lang.ref.Reference */
-    Method*     methJavaLangRefReference_enqueueInternal;
+    /* field offsets - java.lang.ref.FinalizerReference */
+    int offJavaLangRefFinalizerReference_zombie;
 
-    /* more method pointers - java.lang.ref.FinalizerReference */
-    Method*     methJavaLangRefFinalizerReferenceAdd;
+    /* method pointers - java.lang.ref.ReferenceQueue */
+    Method* methJavaLangRefReferenceQueueAdd;
+
+    /* method pointers - java.lang.ref.FinalizerReference */
+    Method* methJavaLangRefFinalizerReferenceAdd;
 
     /* constructor method pointers; no vtable involved, so use Method* */
     Method*     methJavaLangStackTraceElement_init;
@@ -581,19 +584,6 @@
      */
     LinearAllocHdr* pBootLoaderAlloc;
 
-
-    /*
-     * Heap worker thread.
-     */
-    bool            heapWorkerInitialized;
-    bool            heapWorkerReady;
-    bool            haltHeapWorker;
-    pthread_t       heapWorkerHandle;
-    pthread_mutex_t heapWorkerLock;
-    pthread_cond_t  heapWorkerCond;
-    pthread_cond_t  heapWorkerIdleCond;
-    pthread_mutex_t heapWorkerListLock;
-
     /*
      * Compute some stats on loaded classes.
      */
diff --git a/vm/Init.c b/vm/Init.c
index f1afa85..a8cf258 100644
--- a/vm/Init.c
+++ b/vm/Init.c
@@ -1314,6 +1314,9 @@
     if (!dvmDebuggerStartup())
         goto fail;
 
+    if (!dvmGcStartupClasses())
+        goto fail;
+
     /*
      * Init for either zygote mode or non-zygote mode.  The key difference
      * is that we don't start any additional threads in Zygote mode.
diff --git a/vm/InitRefs.c b/vm/InitRefs.c
index b5b7526..5a9ba2a 100644
--- a/vm/InitRefs.c
+++ b/vm/InitRefs.c
@@ -320,6 +320,11 @@
         { NULL, NULL, NULL }
     };
 
+    static struct FieldInfo infoFinalizerReference[] = {
+        { &gDvm.offJavaLangRefFinalizerReference_zombie, "zombie", "Ljava/lang/Object;" },
+        { NULL, NULL, NULL }
+    };
+
     static struct FieldInfo infoConstructor[] = {
         { &gDvm.offJavaLangReflectConstructor_slot,      "slot",           "I" },
         { &gDvm.offJavaLangReflectConstructor_declClass, "declaringClass", "Ljava/lang/Class;" },
@@ -357,6 +362,7 @@
         { "Ljava/lang/ThreadGroup;",                infoThreadGroup },
         { "Ljava/lang/Throwable;",                  infoThrowable },
         { "Ljava/lang/VMThread;",                   infoVMThread },
+        { "Ljava/lang/ref/FinalizerReference;", infoFinalizerReference },
         { "Ljava/lang/reflect/Constructor;",        infoConstructor },
         { "Ljava/lang/reflect/Field;",              infoField },
         { "Ljava/lang/reflect/Method;",             infoMethod },
@@ -468,6 +474,8 @@
           "Lorg/apache/harmony/dalvik/ddmc/DdmServer;", "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;" },
         { &gDvm.methDalvikDdmcServer_broadcast,
           "Lorg/apache/harmony/dalvik/ddmc/DdmServer;", "broadcast", "(I)V" },
+        { &gDvm.methJavaLangRefReferenceQueueAdd,
+          "Ljava/lang/ref/ReferenceQueue;", "add", "(Ljava/lang/ref/Reference;)V" },
         { NULL, NULL, NULL, NULL }
     };
 
@@ -587,26 +595,16 @@
 
 /* (documented in header) */
 bool dvmFindReferenceMembers(ClassObject* classReference) {
-    if (gDvm.methJavaLangRefReference_enqueueInternal != NULL) {
-        LOGE("Attempt to set up class Reference more than once\n");
-        return false;
-    }
-
     if (strcmp(classReference->descriptor, "Ljava/lang/ref/Reference;") != 0) {
         LOGE("Attempt to set up the wrong class as Reference\n");
         return false;
     }
-
-    /* Note: enqueueInternal() is private and thus a direct method. */
-
     return initFieldOffset(classReference, &gDvm.offJavaLangRefReference_pendingNext,
-                "pendingNext", "Ljava/lang/Object;")
+                "pendingNext", "Ljava/lang/ref/Reference;")
         && initFieldOffset(classReference, &gDvm.offJavaLangRefReference_queue,
                 "queue", "Ljava/lang/ref/ReferenceQueue;")
         && initFieldOffset(classReference, &gDvm.offJavaLangRefReference_queueNext,
                 "queueNext", "Ljava/lang/ref/Reference;")
         && initFieldOffset(classReference, &gDvm.offJavaLangRefReference_referent,
-                "referent", "Ljava/lang/Object;")
-        && initDirectMethodReferenceByClass(&gDvm.methJavaLangRefReference_enqueueInternal,
-                classReference, "enqueueInternal", "()Z");
+                "referent", "Ljava/lang/Object;");
 }
diff --git a/vm/Thread.c b/vm/Thread.c
index cdf49bc..087c2e0 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -3467,13 +3467,11 @@
 
 #ifdef HAVE_ANDROID_OS
     dvmPrintDebugMessage(target,
-        "(mutexes: tll=%x tsl=%x tscl=%x ghl=%x hwl=%x hwll=%x)\n",
+        "(mutexes: tll=%x tsl=%x tscl=%x ghl=%x)\n",
         gDvm.threadListLock.value,
         gDvm._threadSuspendLock.value,
         gDvm.threadSuspendCountLock.value,
-        gDvm.gcHeapLock.value,
-        gDvm.heapWorkerLock.value,
-        gDvm.heapWorkerListLock.value);
+        gDvm.gcHeapLock.value);
 #endif
 
     if (grabLock)
diff --git a/vm/alloc/Alloc.c b/vm/alloc/Alloc.c
index 6286f8d..33f45e8 100644
--- a/vm/alloc/Alloc.c
+++ b/vm/alloc/Alloc.c
@@ -20,7 +20,6 @@
 #include "alloc/Heap.h"
 #include "alloc/HeapInternal.h"
 #include "alloc/HeapSource.h"
-#include "alloc/HeapWorker.h"
 
 /*
  * Initialize the GC universe.
@@ -41,9 +40,6 @@
  */
 bool dvmGcStartupAfterZygote(void)
 {
-    if (!dvmHeapWorkerStartup()) {
-        return false;
-    }
     return dvmHeapStartupAfterZygote();
 }
 
@@ -52,7 +48,6 @@
  */
 void dvmGcThreadShutdown(void)
 {
-    dvmHeapWorkerShutdown();
     dvmHeapThreadShutdown();
 }
 
@@ -73,6 +68,44 @@
     return dvmHeapSourceStartupBeforeFork();
 }
 
+bool dvmGcStartupClasses(void)
+{
+    {
+        const char *klassName = "Ljava/lang/ref/ReferenceQueueThread;";
+        ClassObject *klass = dvmFindSystemClass(klassName);
+        if (klass == NULL) {
+            return false;
+        }
+        const char *methodName = "startReferenceQueue";
+        Method *method = dvmFindDirectMethodByDescriptor(klass, methodName, "()V");
+        if (method == NULL) {
+            return false;
+        }
+        Thread *self = dvmThreadSelf();
+        assert(self != NULL);
+        JValue unusedResult;
+        dvmCallMethod(self, method, NULL, &unusedResult);
+    }
+    {
+        const char *klassName = "Ljava/lang/FinalizerThread;";
+        ClassObject *klass = dvmFindSystemClass(klassName);
+        if (klass == NULL) {
+            return false;
+        }
+        const char *methodName = "startFinalizer";
+        Method *method = dvmFindDirectMethodByDescriptor(klass, methodName, "()V");
+        if (method == NULL) {
+            return false;
+        }
+        Thread *self = dvmThreadSelf();
+        assert(self != NULL);
+        JValue unusedResult;
+        dvmCallMethod(self, method, NULL, &unusedResult);
+    }
+
+    return true;
+}
+
 /*
  * Create a "stock instance" of an exception class.
  */
diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h
index 2dc2ae6..aeba3f5 100644
--- a/vm/alloc/Alloc.h
+++ b/vm/alloc/Alloc.h
@@ -29,6 +29,7 @@
 bool dvmGcStartupAfterZygote(void);
 void dvmGcShutdown(void);
 void dvmGcThreadShutdown(void);
+bool dvmGcStartupClasses(void);
 
 /*
  * Do any last-minute preparation before we call fork() for the first time.
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index 5d88bc3..f38eb12 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -19,12 +19,10 @@
 #include "Dalvik.h"
 #include "alloc/HeapBitmap.h"
 #include "alloc/Verify.h"
-#include "alloc/HeapTable.h"
 #include "alloc/Heap.h"
 #include "alloc/HeapInternal.h"
 #include "alloc/DdmHeap.h"
 #include "alloc/HeapSource.h"
-#include "alloc/HeapWorker.h"
 #include "alloc/MarkSweep.h"
 
 #include "utils/threads.h"      // need Android thread priorities
@@ -91,9 +89,6 @@
     if (gcHeap == NULL) {
         return false;
     }
-    gcHeap->heapWorkerCurrentObject = NULL;
-    gcHeap->heapWorkerCurrentMethod = NULL;
-    gcHeap->heapWorkerInterpStartTime = 0LL;
     gcHeap->ddmHpifWhen = 0;
     gcHeap->ddmHpsgWhen = 0;
     gcHeap->ddmHpsgWhat = 0;
@@ -101,22 +96,15 @@
     gcHeap->ddmNhsgWhat = 0;
     gDvm.gcHeap = gcHeap;
 
-    /* Set up the lists and lock we'll use for finalizable
-     * and reference objects.
+    /* Set up the lists we'll use for cleared reference objects.
      */
-    dvmInitMutex(&gDvm.heapWorkerListLock);
-    gcHeap->referenceOperations = NULL;
+    gcHeap->clearedReferences = NULL;
 
     if (!dvmCardTableStartup(gDvm.heapMaximumSize)) {
         LOGE_HEAP("card table startup failed.");
         return false;
     }
 
-    /* Initialize the HeapWorker locks and other state
-     * that the GC uses.
-     */
-    dvmInitializeHeapWorkerState();
-
     return true;
 }
 
@@ -130,13 +118,6 @@
 //TODO: make sure we're locked
     if (gDvm.gcHeap != NULL) {
         dvmCardTableShutdown();
-         /* Tables are allocated on the native heap; they need to be
-         * cleaned up explicitly.  The process may stick around, so we
-         * don't want to leak any native memory.
-         */
-        dvmHeapFreeLargeTable(gDvm.gcHeap->referenceOperations);
-        gDvm.gcHeap->referenceOperations = NULL;
-
         /* Destroy the heap.  Any outstanding pointers will point to
          * unmapped memory (unless/until someone else maps it).  This
          * frees gDvm.gcHeap as a side-effect.
@@ -177,33 +158,6 @@
     dvmUnlockMutex(&gDvm.gcHeapLock);
 }
 
-/* Pop an object from the list of pending finalizations and
- * reference clears/enqueues, and return the object.
- * The caller must call dvmReleaseTrackedAlloc()
- * on the object when finished.
- *
- * Typically only called by the heap worker thread.
- */
-Object *dvmGetNextHeapWorkerObject()
-{
-    Object *obj;
-    GcHeap *gcHeap = gDvm.gcHeap;
-
-    dvmLockMutex(&gDvm.heapWorkerListLock);
-
-    obj = dvmHeapGetNextObjectFromLargeTable(&gcHeap->referenceOperations);
-    if (obj != NULL) {
-        /* Don't let the GC collect the object until the
-         * worker thread is done with it.
-         */
-        dvmAddTrackedAlloc(obj, NULL);
-    }
-
-    dvmUnlockMutex(&gDvm.heapWorkerListLock);
-
-    return obj;
-}
-
 /* Do a full garbage collection, which may grow the
  * heap as a side-effect if the live set is large.
  */
@@ -577,13 +531,6 @@
 
     gcHeap->gcRunning = true;
 
-    /*
-     * Grab the heapWorkerLock to prevent the HeapWorker thread from
-     * doing work.  If it's executing a finalizer or an enqueue operation
-     * it won't be holding the lock, so this should return quickly.
-     */
-    dvmLockMutex(&gDvm.heapWorkerLock);
-
     rootSuspend = dvmGetRelativeTimeMsec();
     dvmSuspendAllThreads(SUSPEND_FOR_GC);
     rootStart = dvmGetRelativeTimeMsec();
@@ -596,21 +543,6 @@
     if (!spec->isConcurrent) {
         oldThreadPriority = raiseThreadPriority();
     }
-
-    /* Make sure that the HeapWorker thread hasn't become
-     * wedged inside interp code.  If it has, this call will
-     * print a message and abort the VM.
-     */
-    dvmAssertHeapWorkerThreadRunning();
-
-    /* Lock the pendingFinalizationRefs list.
-     *
-     * Acquire the lock after suspending so the finalizer
-     * thread can't block in the RUNNING state while
-     * we try to suspend.
-     */
-    dvmLockMutex(&gDvm.heapWorkerListLock);
-
     if (gDvm.preVerify) {
         LOGV_HEAP("Verifying roots and heap before GC");
         verifyRootsAndHeap();
@@ -634,9 +566,11 @@
     /* dvmHeapScanMarkedObjects() will build the lists of known
      * instances of the Reference classes.
      */
-    gcHeap->softReferences = NULL;
-    gcHeap->weakReferences = NULL;
-    gcHeap->phantomReferences = NULL;
+    assert(gcHeap->softReferences == NULL);
+    assert(gcHeap->weakReferences == NULL);
+    assert(gcHeap->finalizerReferences == NULL);
+    assert(gcHeap->phantomReferences == NULL);
+    assert(gcHeap->clearedReferences == NULL);
 
     if (spec->isConcurrent) {
         /*
@@ -747,25 +681,12 @@
     currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
     currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
 
-    /* Now that we've freed up the GC heap, return any large
-     * free chunks back to the system.  They'll get paged back
-     * in the next time they're used.  Don't do it immediately,
-     * though;  if the process is still allocating a bunch of
-     * memory, we'll be taking a ton of page faults that we don't
-     * necessarily need to.
-     *
-     * Cancel any old scheduled trims, and schedule a new one.
-     */
-    dvmScheduleHeapSourceTrim(5);  // in seconds
-
     dvmMethodTraceGCEnd();
     LOGV_HEAP("GC finished");
 
     gcHeap->gcRunning = false;
 
     LOGV_HEAP("Resuming threads");
-    dvmUnlockMutex(&gDvm.heapWorkerListLock);
-    dvmUnlockMutex(&gDvm.heapWorkerLock);
 
     if (spec->isConcurrent) {
         /*
@@ -787,6 +708,11 @@
         }
     }
 
+    /*
+     * Move queue of pending references back into Java.
+     */
+    dvmEnqueueClearedReferences(&gDvm.gcHeap->clearedReferences);
+
     percentFree = 100 - (size_t)(100.0f * (float)currAllocated / currFootprint);
     if (!spec->isConcurrent) {
         u4 markSweepTime = dirtyEnd - rootStart;
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
index 7f8c9c5..1d1be07 100644
--- a/vm/alloc/HeapInternal.h
+++ b/vm/alloc/HeapInternal.h
@@ -19,9 +19,6 @@
 #ifndef _DALVIK_ALLOC_HEAP_INTERNAL
 #define _DALVIK_ALLOC_HEAP_INTERNAL
 
-#include <time.h>  // for struct timespec
-
-#include "HeapTable.h"
 #include "MarkSweep.h"
 
 typedef struct HeapSource HeapSource;
@@ -39,34 +36,9 @@
     Object *finalizerReferences;
     Object *phantomReferences;
 
-    /* The list of Reference objects that need to be cleared and/or
-     * enqueued.  The bottom two bits of the object pointers indicate
-     * whether they should be cleared and/or enqueued.
-     *
-     * This table is protected by gDvm.heapWorkerListLock, which must
-     * be acquired after the heap lock.
+    /* The list of Reference objects that need to be enqueued.
      */
-    LargeHeapRefTable  *referenceOperations;
-
-    /* If non-null, the method that the HeapWorker is currently
-     * executing.
-     */
-    Object *heapWorkerCurrentObject;
-    Method *heapWorkerCurrentMethod;
-
-    /* If heapWorkerCurrentObject is non-null, this gives the time when
-     * HeapWorker started executing that method.  The time value must come
-     * from dvmGetRelativeTimeUsec().
-     *
-     * The "Cpu" entry tracks the per-thread CPU timer (when available).
-     */
-    u8 heapWorkerInterpStartTime;
-    u8 heapWorkerInterpCpuStartTime;
-
-    /* If any fields are non-zero, indicates the next (absolute) time that
-     * the HeapWorker thread should call dvmHeapSourceTrim().
-     */
-    struct timespec heapWorkerNextTrim;
+    Object *clearedReferences;
 
     /* The current state of the mark step.
      * Only valid during a GC.
diff --git a/vm/alloc/HeapTable.c b/vm/alloc/HeapTable.c
deleted file mode 100644
index 6c9034a..0000000
--- a/vm/alloc/HeapTable.c
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Dalvik.h"
-#include "alloc/HeapTable.h"
-#include "alloc/HeapInternal.h"
-
-#include <limits.h> // for INT_MAX
-
-static const int kLargeHeapRefTableNElems = 1024;
-static const int  kFinalizableRefDefault = 128;
-
-bool dvmHeapInitHeapRefTable(ReferenceTable *refs)
-{
-    return dvmInitReferenceTable(refs, kFinalizableRefDefault, INT_MAX);
-}
-
-/*
- * Large, non-contiguous reference tables
- */
-
-bool dvmHeapAddRefToLargeTable(LargeHeapRefTable **tableP, Object *ref)
-{
-    LargeHeapRefTable *table;
-
-    assert(tableP != NULL);
-    assert(ref != NULL);
-
-    /* Make sure that a table with a free slot is
-     * at the head of the list.
-     */
-    if (*tableP != NULL) {
-        table = *tableP;
-        LargeHeapRefTable *prevTable;
-
-        /* Find an empty slot for this reference.
-         */
-        prevTable = NULL;
-        while (table != NULL && dvmIsReferenceTableFull(&table->refs)) {
-            prevTable = table;
-            table = table->next;
-        }
-        if (table != NULL) {
-            if (prevTable != NULL) {
-                /* Move the table to the head of the list.
-                 */
-                prevTable->next = table->next;
-                table->next = *tableP;
-                *tableP = table;
-            }
-            /* else it's already at the head. */
-
-            goto insert;
-        }
-        /* else all tables are already full;
-         * fall through to the alloc case.
-         */
-    }
-
-    /* Allocate a new table.
-     */
-    table = (LargeHeapRefTable *)calloc(1, sizeof(LargeHeapRefTable));
-    if (table == NULL) {
-        LOGE_HEAP("Can't allocate a new large ref table\n");
-        return false;
-    }
-    if (!dvmInitReferenceTable(&table->refs,
-                               kLargeHeapRefTableNElems,
-                               INT_MAX)) {
-        LOGE_HEAP("Can't initialize a new large ref table\n");
-        free(table);
-        return false;
-    }
-
-    /* Stick it at the head.
-     */
-    table->next = *tableP;
-    *tableP = table;
-
-insert:
-    /* Insert the reference.
-     */
-    assert(table == *tableP);
-    assert(table != NULL);
-    assert(!dvmIsReferenceTableFull(&table->refs));
-    *table->refs.nextEntry++ = ref;
-
-    return true;
-}
-
-bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP, ReferenceTable *refs)
-{
-    LargeHeapRefTable *table;
-
-    /* Allocate a node.
-     */
-    table = (LargeHeapRefTable *)calloc(1, sizeof(LargeHeapRefTable));
-    if (table == NULL) {
-        LOGE_HEAP("Can't allocate a new large ref table\n");
-        return false;
-    }
-    table->refs = *refs;
-
-    /* Insert the table into the list.
-     */
-    table->next = *tableP;
-    *tableP = table;
-
-    return true;
-}
-
-/* Frees everything associated with the LargeHeapRefTable.
- */
-void dvmHeapFreeLargeTable(LargeHeapRefTable *table)
-{
-    while (table != NULL) {
-        LargeHeapRefTable *next = table->next;
-        dvmClearReferenceTable(&table->refs);
-        free(table);
-        table = next;
-    }
-}
-
-Object *dvmHeapGetNextObjectFromLargeTable(LargeHeapRefTable **pTable)
-{
-    LargeHeapRefTable *table;
-    Object *obj;
-
-    assert(pTable != NULL);
-
-    obj = NULL;
-    table = *pTable;
-    if (table != NULL) {
-        ReferenceTable *refs = &table->refs;
-
-        /* We should never have an empty table node in the list.
-         */
-        assert(dvmReferenceTableEntries(refs) != 0);
-
-        /* Remove and return the last entry in the list.
-         */
-        obj = *--refs->nextEntry;
-
-        /* If this was the last entry in the table node,
-         * free it and patch up the list.
-         */
-        if (refs->nextEntry == refs->table) {
-            *pTable = table->next;
-            dvmClearReferenceTable(refs);
-            free(table);
-        }
-    }
-
-    return obj;
-}
diff --git a/vm/alloc/HeapTable.h b/vm/alloc/HeapTable.h
deleted file mode 100644
index 175111d..0000000
--- a/vm/alloc/HeapTable.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef _DALVIK_ALLOC_HEAP_TABLE
-#define _DALVIK_ALLOC_HEAP_TABLE
-
-#include "ReferenceTable.h"
-
-struct LargeHeapRefTable {
-    struct LargeHeapRefTable *next;
-    ReferenceTable refs;
-};
-
-typedef struct LargeHeapRefTable LargeHeapRefTable;
-
-bool dvmHeapInitHeapRefTable(ReferenceTable *refs);
-void dvmHeapFreeLargeTable(LargeHeapRefTable *table);
-bool dvmHeapAddRefToLargeTable(LargeHeapRefTable **tableP, Object *ref);
-void dvmHeapMarkLargeTableRefs(LargeHeapRefTable *table);
-bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP,
-        ReferenceTable *refs);
-Object *dvmHeapGetNextObjectFromLargeTable(LargeHeapRefTable **pTable);
-
-#endif  // _DALVIK_ALLOC_HEAP_TABLE
diff --git a/vm/alloc/HeapWorker.c b/vm/alloc/HeapWorker.c
deleted file mode 100644
index 6017381..0000000
--- a/vm/alloc/HeapWorker.c
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * An async worker thread to handle certain heap operations that need
- * to be done in a separate thread to avoid synchronization problems.
- * HeapWorkers and reference enqueuing are handled by this thread.
- * The VM does all clearing.
- */
-#include "Dalvik.h"
-#include "alloc/HeapInternal.h"
-#include "alloc/HeapWorker.h"
-
-#include <sys/time.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <signal.h>
-#include <errno.h>  // for ETIMEDOUT, etc.
-
-static void* heapWorkerThreadStart(void* arg);
-
-/*
- * Initialize any HeapWorker state that Heap.c
- * cares about.  This lets the GC start before the
- * HeapWorker thread is initialized.
- */
-void dvmInitializeHeapWorkerState()
-{
-    assert(!gDvm.heapWorkerInitialized);
-
-    dvmInitMutex(&gDvm.heapWorkerLock);
-    pthread_cond_init(&gDvm.heapWorkerCond, NULL);
-    pthread_cond_init(&gDvm.heapWorkerIdleCond, NULL);
-
-    gDvm.heapWorkerInitialized = true;
-}
-
-/*
- * Crank up the heap worker thread.
- *
- * Does not return until the thread is ready for business.
- */
-bool dvmHeapWorkerStartup(void)
-{
-    assert(!gDvm.haltHeapWorker);
-    assert(!gDvm.heapWorkerReady);
-    assert(gDvm.heapWorkerHandle == 0);
-    assert(gDvm.heapWorkerInitialized);
-
-    /* use heapWorkerLock/heapWorkerCond to communicate readiness */
-    dvmLockMutex(&gDvm.heapWorkerLock);
-
-//BUG: If a GC happens in here or in the new thread while we hold the lock,
-//     the GC will deadlock when trying to acquire heapWorkerLock.
-    if (!dvmCreateInternalThread(&gDvm.heapWorkerHandle,
-                "HeapWorker", heapWorkerThreadStart, NULL))
-    {
-        dvmUnlockMutex(&gDvm.heapWorkerLock);
-        return false;
-    }
-
-    /*
-     * Wait for the heap worker to come up.  We know the thread was created,
-     * so this should not get stuck.
-     */
-    while (!gDvm.heapWorkerReady) {
-        dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
-    }
-
-    dvmUnlockMutex(&gDvm.heapWorkerLock);
-    return true;
-}
-
-/*
- * Shut down the heap worker thread if it was started.
- */
-void dvmHeapWorkerShutdown(void)
-{
-    void* threadReturn;
-
-    /* note: assuming that (pthread_t)0 is not a valid thread handle */
-    if (gDvm.heapWorkerHandle != 0) {
-        gDvm.haltHeapWorker = true;
-        dvmSignalHeapWorker(true);
-
-        /*
-         * We may not want to wait for the heapWorkers to complete.  It's
-         * a good idea to do so, in case they're holding some sort of OS
-         * resource that doesn't get reclaimed when the process exits
-         * (e.g. an open temp file).
-         */
-        if (pthread_join(gDvm.heapWorkerHandle, &threadReturn) != 0)
-            LOGW("HeapWorker thread join failed\n");
-        else if (gDvm.verboseShutdown)
-            LOGD("HeapWorker thread has shut down\n");
-
-        gDvm.heapWorkerReady = false;
-    }
-}
-
-/* Make sure that the HeapWorker thread hasn't spent an inordinate
- * amount of time inside a finalizer.
- *
- * Aborts the VM if the thread appears to be wedged.
- *
- * The caller must hold the heapWorkerLock to guarantee an atomic
- * read of the watchdog values.
- */
-void dvmAssertHeapWorkerThreadRunning()
-{
-    if (gDvm.gcHeap->heapWorkerCurrentObject != NULL) {
-        static const u8 HEAP_WORKER_WATCHDOG_TIMEOUT = 10*1000*1000LL; // 10sec
-
-        u8 heapWorkerInterpStartTime = gDvm.gcHeap->heapWorkerInterpStartTime;
-        u8 now = dvmGetRelativeTimeUsec();
-        u8 delta = now - heapWorkerInterpStartTime;
-
-        if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT &&
-            (gDvm.debuggerActive || gDvm.nativeDebuggerActive))
-        {
-            /*
-             * Debugger suspension can block the thread indefinitely.  For
-             * best results we should reset this explicitly whenever the
-             * HeapWorker thread is resumed.  Unfortunately this is also
-             * affected by native debuggers, and we have no visibility
-             * into how they're manipulating us.  So, we ignore the
-             * watchdog and just reset the timer.
-             */
-            LOGI("Debugger is attached -- suppressing HeapWorker watchdog\n");
-            gDvm.gcHeap->heapWorkerInterpStartTime = now;   /* reset timer */
-        } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT) {
-            /*
-             * Before we give up entirely, see if maybe we're just not
-             * getting any CPU time because we're stuck in a background
-             * process group.  If we successfully move the thread into the
-             * foreground we'll just leave it there (it doesn't do anything
-             * if the process isn't GCing).
-             */
-            dvmLockThreadList(NULL);
-            Thread* thread = dvmGetThreadByHandle(gDvm.heapWorkerHandle);
-            dvmUnlockThreadList();
-
-            if (thread != NULL) {
-                int priChangeFlags, threadPrio;
-                SchedPolicy threadPolicy;
-                priChangeFlags = dvmRaiseThreadPriorityIfNeeded(thread,
-                        &threadPrio, &threadPolicy);
-                if (priChangeFlags != 0) {
-                    LOGI("HeapWorker watchdog expired, raising priority"
-                         " and retrying\n");
-                    gDvm.gcHeap->heapWorkerInterpStartTime = now;
-                    return;
-                }
-            }
-
-            char* desc = dexProtoCopyMethodDescriptor(
-                    &gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
-            LOGE("HeapWorker is wedged: %lldms spent inside %s.%s%s\n",
-                    delta / 1000,
-                    gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
-                    gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
-            free(desc);
-            dvmDumpAllThreads(true);
-
-            /* try to get a debuggerd dump from the target thread */
-            dvmNukeThread(thread);
-
-            /* abort the VM */
-            dvmAbort();
-        } else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT / 2) {
-            char* desc = dexProtoCopyMethodDescriptor(
-                    &gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
-            LOGW("HeapWorker may be wedged: %lldms spent inside %s.%s%s\n",
-                    delta / 1000,
-                    gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
-                    gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
-            free(desc);
-        }
-    }
-}
-
-/*
- * Acquires a mutex, transitioning to the VMWAIT state if the mutex is
- * held.  This allows the thread to suspend while it waits for another
- * thread to release the mutex.
- */
-static void lockMutex(pthread_mutex_t *mu)
-{
-    Thread *self;
-    ThreadStatus oldStatus;
-
-    assert(mu != NULL);
-    if (dvmTryLockMutex(mu) != 0) {
-        self = dvmThreadSelf();
-        assert(self != NULL);
-        oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
-        dvmLockMutex(mu);
-        dvmChangeStatus(self, oldStatus);
-    }
-}
-
-static void callMethod(Thread *self, Object *obj, Method *method)
-{
-    JValue unused;
-
-    /* Keep track of the method we're about to call and
-     * the current time so that other threads can detect
-     * when this thread wedges and provide useful information.
-     */
-    gDvm.gcHeap->heapWorkerInterpStartTime = dvmGetRelativeTimeUsec();
-    gDvm.gcHeap->heapWorkerInterpCpuStartTime = dvmGetThreadCpuTimeUsec();
-    gDvm.gcHeap->heapWorkerCurrentMethod = method;
-    gDvm.gcHeap->heapWorkerCurrentObject = obj;
-
-    /* Call the method.
-     *
-     * Don't hold the lock when executing interpreted
-     * code.  It may suspend, and the GC needs to grab
-     * heapWorkerLock.
-     */
-    dvmUnlockMutex(&gDvm.heapWorkerLock);
-    if (false) {
-        /* Log entry/exit; this will likely flood the log enough to
-         * cause "logcat" to drop entries.
-         */
-        char tmpTag[16];
-        sprintf(tmpTag, "HW%d", self->systemTid);
-        LOG(LOG_DEBUG, tmpTag, "Call %s\n", method->clazz->descriptor);
-        dvmCallMethod(self, method, obj, &unused);
-        LOG(LOG_DEBUG, tmpTag, " done\n");
-    } else {
-        dvmCallMethod(self, method, obj, &unused);
-    }
-    /*
-     * Reacquire the heap worker lock in a suspend-friendly way.
-     */
-    lockMutex(&gDvm.heapWorkerLock);
-
-    gDvm.gcHeap->heapWorkerCurrentObject = NULL;
-    gDvm.gcHeap->heapWorkerCurrentMethod = NULL;
-    gDvm.gcHeap->heapWorkerInterpStartTime = 0LL;
-
-    /* Exceptions thrown during these calls interrupt
-     * the method, but are otherwise ignored.
-     */
-    if (dvmCheckException(self)) {
-#if DVM_SHOW_EXCEPTION >= 1
-        LOGI("Uncaught exception thrown by finalizer (will be discarded):\n");
-        dvmLogExceptionStackTrace();
-#endif
-        dvmClearException(self);
-    }
-}
-
-/* Process all enqueued heap work, including finalizers and reference
- * enqueueing. Clearing has already been done by the VM.
- *
- * Caller must hold gDvm.heapWorkerLock.
- */
-static void doHeapWork(Thread *self)
-{
-    Object *obj;
-    size_t numReferencesEnqueued;
-
-    assert(gDvm.methJavaLangRefReference_enqueueInternal != NULL);
-    numReferencesEnqueued = 0;
-    while ((obj = dvmGetNextHeapWorkerObject()) != NULL) {
-        /* Make sure the object hasn't been collected since
-         * being scheduled.
-         */
-        assert(dvmIsValidObject(obj));
-
-        /* Call the appropriate method(s).
-         */
-        assert(dvmGetFieldObject(
-                   obj, gDvm.offJavaLangRefReference_queue) != NULL);
-        assert(dvmGetFieldObject(
-                   obj, gDvm.offJavaLangRefReference_queueNext) == NULL);
-        numReferencesEnqueued++;
-        callMethod(self, obj, gDvm.methJavaLangRefReference_enqueueInternal);
-
-        /* Let the GC collect the object.
-         */
-        dvmReleaseTrackedAlloc(obj, self);
-    }
-    LOGV("Enqueued %zd references", numReferencesEnqueued);
-}
-
-/*
- * The heap worker thread sits quietly until the GC tells it there's work
- * to do.
- */
-static void* heapWorkerThreadStart(void* arg)
-{
-    Thread *self = dvmThreadSelf();
-
-    UNUSED_PARAMETER(arg);
-
-    LOGV("HeapWorker thread started (threadid=%d)\n", self->threadId);
-
-    /* tell the main thread that we're ready */
-    lockMutex(&gDvm.heapWorkerLock);
-    gDvm.heapWorkerReady = true;
-    dvmSignalCond(&gDvm.heapWorkerCond);
-    dvmUnlockMutex(&gDvm.heapWorkerLock);
-
-    lockMutex(&gDvm.heapWorkerLock);
-    while (!gDvm.haltHeapWorker) {
-        struct timespec trimtime;
-        bool timedwait = false;
-
-        /* We're done running interpreted code for now. */
-        dvmChangeStatus(NULL, THREAD_VMWAIT);
-
-        /* Signal anyone who wants to know when we're done. */
-        dvmBroadcastCond(&gDvm.heapWorkerIdleCond);
-
-        /* Trim the heap if we were asked to. */
-        trimtime = gDvm.gcHeap->heapWorkerNextTrim;
-        if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) {
-            struct timespec now;
-
-#ifdef HAVE_TIMEDWAIT_MONOTONIC
-            clock_gettime(CLOCK_MONOTONIC, &now);       // relative time
-#else
-            struct timeval tvnow;
-            gettimeofday(&tvnow, NULL);                 // absolute time
-            now.tv_sec = tvnow.tv_sec;
-            now.tv_nsec = tvnow.tv_usec * 1000;
-#endif
-
-            if (trimtime.tv_sec < now.tv_sec ||
-                (trimtime.tv_sec == now.tv_sec &&
-                 trimtime.tv_nsec <= now.tv_nsec))
-            {
-                size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT];
-
-                /*
-                 * Acquire the gcHeapLock.  The requires releasing the
-                 * heapWorkerLock before the gcHeapLock is acquired.
-                 * It is possible that the gcHeapLock may be acquired
-                 * during a concurrent GC in which case heapWorkerLock
-                 * is held by the GC and we are unable to make forward
-                 * progress.  We avoid deadlock by releasing the
-                 * gcHeapLock and then waiting to be signaled when the
-                 * GC completes.  There is no guarantee that the next
-                 * time we are run will coincide with GC inactivity so
-                 * the check and wait must be performed within a loop.
-                 */
-                dvmUnlockMutex(&gDvm.heapWorkerLock);
-                dvmLockHeap();
-                dvmWaitForConcurrentGcToComplete();
-                dvmLockMutex(&gDvm.heapWorkerLock);
-
-                memset(madvisedSizes, 0, sizeof(madvisedSizes));
-                dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
-
-                dvmUnlockHeap();
-
-                trimtime.tv_sec = 0;
-                trimtime.tv_nsec = 0;
-                gDvm.gcHeap->heapWorkerNextTrim = trimtime;
-            } else {
-                timedwait = true;
-            }
-        }
-
-        /* sleep until signaled */
-        if (timedwait) {
-            int cc __attribute__ ((__unused__));
-#ifdef HAVE_TIMEDWAIT_MONOTONIC
-            cc = pthread_cond_timedwait_monotonic(&gDvm.heapWorkerCond,
-                    &gDvm.heapWorkerLock, &trimtime);
-#else
-            cc = pthread_cond_timedwait(&gDvm.heapWorkerCond,
-                    &gDvm.heapWorkerLock, &trimtime);
-#endif
-            assert(cc == 0 || cc == ETIMEDOUT);
-        } else {
-            dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
-        }
-
-        /*
-         * Return to the running state before doing heap work.  This
-         * will block if the GC has initiated a suspend.  We release
-         * the heapWorkerLock beforehand for the GC to make progress
-         * and wait to be signaled after the GC completes.  There is
-         * no guarantee that the next time we are run will coincide
-         * with GC inactivity so the check and wait must be performed
-         * within a loop.
-         */
-        dvmUnlockMutex(&gDvm.heapWorkerLock);
-        dvmChangeStatus(NULL, THREAD_RUNNING);
-        dvmLockHeap();
-        dvmWaitForConcurrentGcToComplete();
-        dvmLockMutex(&gDvm.heapWorkerLock);
-        dvmUnlockHeap();
-        LOGV("HeapWorker is awake\n");
-
-        /* Process any events in the queue.
-         */
-        doHeapWork(self);
-    }
-    dvmUnlockMutex(&gDvm.heapWorkerLock);
-
-    if (gDvm.verboseShutdown)
-        LOGD("HeapWorker thread shutting down\n");
-    return NULL;
-}
-
-/*
- * Wake up the heap worker to let it know that there's work to be done.
- */
-void dvmSignalHeapWorker(bool shouldLock)
-{
-    if (shouldLock) {
-        dvmLockMutex(&gDvm.heapWorkerLock);
-    }
-
-    dvmSignalCond(&gDvm.heapWorkerCond);
-
-    if (shouldLock) {
-        dvmUnlockMutex(&gDvm.heapWorkerLock);
-    }
-}
-
-/*
- * Requests that dvmHeapSourceTrim() be called no sooner
- * than timeoutSec seconds from now.  If timeoutSec
- * is zero, any pending trim is cancelled.
- *
- * Caller must hold heapWorkerLock.
- */
-void dvmScheduleHeapSourceTrim(size_t timeoutSec)
-{
-    GcHeap *gcHeap = gDvm.gcHeap;
-    struct timespec timeout;
-
-    if (timeoutSec == 0) {
-        timeout.tv_sec = 0;
-        timeout.tv_nsec = 0;
-        /* Don't wake up the thread just to tell it to cancel.
-         * If it wakes up naturally, we can avoid the extra
-         * context switch.
-         */
-    } else {
-#ifdef HAVE_TIMEDWAIT_MONOTONIC
-        clock_gettime(CLOCK_MONOTONIC, &timeout);
-        timeout.tv_sec += timeoutSec;
-#else
-        struct timeval now;
-        gettimeofday(&now, NULL);
-        timeout.tv_sec = now.tv_sec + timeoutSec;
-        timeout.tv_nsec = now.tv_usec * 1000;
-#endif
-        dvmSignalHeapWorker(false);
-    }
-    gcHeap->heapWorkerNextTrim = timeout;
-}
diff --git a/vm/alloc/HeapWorker.h b/vm/alloc/HeapWorker.h
deleted file mode 100644
index 67babc3..0000000
--- a/vm/alloc/HeapWorker.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-/*
- * Manage async heap tasks.
- */
-#ifndef _DALVIK_ALLOC_HEAP_WORKER
-#define _DALVIK_ALLOC_HEAP_WORKER
-
-/*
- * Initialize any HeapWorker state that Heap.c
- * cares about.  This lets the GC start before the
- * HeapWorker thread is initialized.
- */
-void dvmInitializeHeapWorkerState(void);
-
-/*
- * Initialization.  Starts/stops the worker thread.
- */
-bool dvmHeapWorkerStartup(void);
-void dvmHeapWorkerShutdown(void);
-
-/*
- * Tell the worker thread to wake up and do work.
- * If shouldLock is false, the caller must have already
- * acquired gDvm.heapWorkerLock.
- */
-void dvmSignalHeapWorker(bool shouldLock);
-
-/*
- * Requests that dvmHeapSourceTrim() be called no sooner
- * than timeoutSec seconds from now.  If timeoutSec
- * is zero, any pending trim is cancelled.
- *
- * Caller must hold heapWorkerLock.
- */
-void dvmScheduleHeapSourceTrim(size_t timeoutSec);
-
-/* Make sure that the HeapWorker thread hasn't spent an inordinate
- * amount of time inside interpreted code.
- *
- * Aborts the VM if the thread appears to be wedged.
- *
- * The caller must hold the heapWorkerLock.
- */
-void dvmAssertHeapWorkerThreadRunning();
-
-/*
- * Called by the worker thread to get the next object
- * to finalize/enqueue/clear.  Implemented in Heap.c.
- *
- * @param op The operation to perform on the returned object.
- *           Must be non-NULL.
- * @return The object to operate on, or NULL.
- */
-Object *dvmGetNextHeapWorkerObject();
-
-#endif /*_DALVIK_ALLOC_HEAP_WORKER*/
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index 69b75de..e26394c 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -20,7 +20,6 @@
 #include "alloc/HeapBitmapInlines.h"
 #include "alloc/HeapInternal.h"
 #include "alloc/HeapSource.h"
-#include "alloc/HeapWorker.h"
 #include "alloc/MarkSweep.h"
 #include "alloc/Visit.h"
 #include "alloc/VisitInlines.h"
@@ -342,8 +341,6 @@
  * - 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:
@@ -795,11 +792,7 @@
     assert(ref != NULL);
     assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queue) != NULL);
     assert(dvmGetFieldObject(ref, gDvm.offJavaLangRefReference_queueNext) == NULL);
-    if (!dvmHeapAddRefToLargeTable(&gDvm.gcHeap->referenceOperations, ref)) {
-        LOGE_HEAP("enqueueReference(): no room for any more "
-                  "reference operations\n");
-        dvmAbort();
-    }
+    enqueuePendingReference(ref, &gDvm.gcHeap->clearedReferences);
 }
 
 /*
@@ -857,11 +850,9 @@
     GcMarkContext *ctx;
     Object *ref, *referent;
     size_t referentOffset;
-    bool doSignal;
 
     ctx = &gDvm.gcHeap->markContext;
     referentOffset = gDvm.offJavaLangRefReference_referent;
-    doSignal = false;
     while (*list != NULL) {
         ref = dequeuePendingReference(list);
         referent = dvmGetFieldObject(ref, referentOffset);
@@ -870,31 +861,23 @@
             clearReference(ref);
             if (isEnqueuable(ref)) {
                 enqueueReference(ref);
-                doSignal = true;
             }
         }
     }
-    /*
-     * 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);
 }
 
 /*
  * Enqueues finalizer references with white referents.  White
- * referents are blackened, moved to the pendingNext field, and the
+ * referents are blackened, moved to the zombie field, and the
  * referent field is cleared.
  */
 static void enqueueFinalizerReferences(Object **list)
 {
     GcMarkContext *ctx = &gDvm.gcHeap->markContext;
     size_t referentOffset = gDvm.offJavaLangRefReference_referent;
-    size_t pendingNextOffset = gDvm.offJavaLangRefReference_pendingNext;
-    bool doSignal = false;
+    size_t zombieOffset = gDvm.offJavaLangRefFinalizerReference_zombie;
+    bool hasEnqueued = false;
     while (*list != NULL) {
         Object *ref = dequeuePendingReference(list);
         Object *referent = dvmGetFieldObject(ref, referentOffset);
@@ -902,15 +885,14 @@
             markObject(referent, ctx);
             /* If the referent is non-null the reference must queuable. */
             assert(isEnqueuable(ref));
-            dvmSetFieldObject(ref, pendingNextOffset, referent);
+            dvmSetFieldObject(ref, zombieOffset, referent);
             clearReference(ref);
             enqueueReference(ref);
-            doSignal = true;
+            hasEnqueued = true;
         }
     }
-    if (doSignal) {
+    if (hasEnqueued) {
         processMarkStack(ctx);
-        dvmSignalHeapWorker(false);
     }
     assert(*list == NULL);
 }
@@ -928,8 +910,8 @@
     assert(self != NULL);
     Method *meth = gDvm.methJavaLangRefFinalizerReferenceAdd;
     assert(meth != NULL);
-    JValue unused;
-    dvmCallMethod(self, meth, obj, &unused, obj);
+    JValue unusedResult;
+    dvmCallMethod(self, meth, NULL, &unusedResult, obj);
 }
 
 /*
@@ -982,6 +964,24 @@
     assert(*phantomReferences == NULL);
 }
 
+/*
+ * Pushes a list of cleared references out to the managed heap.
+ */
+void dvmEnqueueClearedReferences(Object **cleared)
+{
+    assert(cleared != NULL);
+    if (*cleared != NULL) {
+        Thread *self = dvmThreadSelf();
+        assert(self != NULL);
+        Method *meth = gDvm.methJavaLangRefReferenceQueueAdd;
+        assert(meth != NULL);
+        JValue unused;
+        Object *reference = *cleared;
+        dvmCallMethod(self, meth, NULL, &unused, reference);
+        *cleared = NULL;
+    }
+}
+
 void dvmHeapFinishMarkStep()
 {
     GcMarkContext *ctx;
diff --git a/vm/alloc/MarkSweep.h b/vm/alloc/MarkSweep.h
index c9f11e4..94bf3ad 100644
--- a/vm/alloc/MarkSweep.h
+++ b/vm/alloc/MarkSweep.h
@@ -59,5 +59,6 @@
 void dvmHeapSweepSystemWeaks(void);
 void dvmHeapSweepUnmarkedObjects(bool isPartial, bool isConcurrent,
                                  size_t *numObjects, size_t *numBytes);
+void dvmEnqueueClearedReferences(Object **references);
 
 #endif  // _DALVIK_ALLOC_MARK_SWEEP
diff --git a/vm/alloc/Visit.c b/vm/alloc/Visit.c
index 634611c..6ca1439 100644
--- a/vm/alloc/Visit.c
+++ b/vm/alloc/Visit.c
@@ -83,20 +83,6 @@
 }
 
 /*
- * Visits a large heap reference table.  These objects are list heads.
- * As such, it is valid for table to be NULL.
- */
-static void visitLargeHeapRefTable(RootVisitor *visitor,
-                                   LargeHeapRefTable *table,
-                                   RootType type, void *arg)
-{
-    assert(visitor != NULL);
-    for (; table != NULL; table = table->next) {
-        visitReferenceTable(visitor, &table->refs, 0, type, arg);
-    }
-}
-
-/*
  * Visits all stack slots except those belonging to native method
  * arguments.
  */
@@ -250,7 +236,6 @@
     dvmLockMutex(&gDvm.jniPinRefLock);
     visitReferenceTable(visitor, &gDvm.jniPinRefTable, 0, ROOT_VM_INTERNAL, arg);
     dvmUnlockMutex(&gDvm.jniPinRefLock);
-    visitLargeHeapRefTable(visitor, gDvm.gcHeap->referenceOperations, ROOT_REFERENCE_CLEANUP, arg);
     visitThreads(visitor, arg);
     (*visitor)(&gDvm.outOfMemoryObj, 0, ROOT_VM_INTERNAL, arg);
     (*visitor)(&gDvm.internalErrorObj, 0, ROOT_VM_INTERNAL, arg);
diff --git a/vm/alloc/Visit.h b/vm/alloc/Visit.h
index e7c52e5..a66839d 100644
--- a/vm/alloc/Visit.h
+++ b/vm/alloc/Visit.h
@@ -30,9 +30,7 @@
   ROOT_MONITOR_USED,
   ROOT_THREAD_OBJECT,
   ROOT_INTERNED_STRING,
-  ROOT_FINALIZING,
   ROOT_DEBUGGER,
-  ROOT_REFERENCE_CLEANUP,
   ROOT_VM_INTERNAL,
   ROOT_JNI_MONITOR,
 } RootType;
diff --git a/vm/hprof/Hprof.h b/vm/hprof/Hprof.h
index c79a3df..c62dd9c 100644
--- a/vm/hprof/Hprof.h
+++ b/vm/hprof/Hprof.h
@@ -84,12 +84,12 @@
     /* Android */
     HPROF_HEAP_DUMP_INFO = 0xfe,
     HPROF_ROOT_INTERNED_STRING = 0x89,
-    HPROF_ROOT_FINALIZING = 0x8a,
+    HPROF_ROOT_FINALIZING = 0x8a,  /* obsolete */
     HPROF_ROOT_DEBUGGER = 0x8b,
-    HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,
+    HPROF_ROOT_REFERENCE_CLEANUP = 0x8c,  /* obsolete */
     HPROF_ROOT_VM_INTERNAL = 0x8d,
     HPROF_ROOT_JNI_MONITOR = 0x8e,
-    HPROF_UNREACHABLE = 0x90,  /* deprecated */
+    HPROF_UNREACHABLE = 0x90,  /* obsolete */
     HPROF_PRIMITIVE_ARRAY_NODATA_DUMP = 0xc3,
 } hprof_heap_tag_t;