Drive all root scanning by the root visitor.

The root visitor has been used by the concurrent collector during the
re-mark phase.  This change makes both the initial mark use the same
visitor routine and obsoletes all of the one-off markers scattered
throughout the runtime sources.

Change-Id: I08ea86d875f235cc628754240ad30ea5dfe2ce70
diff --git a/vm/Debugger.c b/vm/Debugger.c
index 39eb74a..d5215ca 100644
--- a/vm/Debugger.c
+++ b/vm/Debugger.c
@@ -238,32 +238,6 @@
 }
 
 /*
- * (This is a HashForeachFunc callback.)
- */
-static int markRef(void* data, void* arg)
-{
-    UNUSED_PARAMETER(arg);
-
-    //LOGI("dbg mark %p\n", data);
-    dvmMarkObjectNonNull(data);
-    return 0;
-}
-
-/* Mark all of the registered debugger references so the
- * GC doesn't collect them.
- */
-void dvmGcMarkDebuggerRefs()
-{
-    /* dvmDebuggerStartup() may not have been called before the first GC.
-     */
-    if (gDvm.dbgRegistry != NULL) {
-        dvmHashTableLock(gDvm.dbgRegistry);
-        dvmHashForeach(gDvm.dbgRegistry, markRef, NULL);
-        dvmHashTableUnlock(gDvm.dbgRegistry);
-    }
-}
-
-/*
  * Verify that an object has been registered.  If it hasn't, the debugger
  * is asking for something we didn't send it, which means something
  * somewhere is broken.
diff --git a/vm/Intern.c b/vm/Intern.c
index 8bc38b8..f91cc35 100644
--- a/vm/Intern.c
+++ b/vm/Intern.c
@@ -169,27 +169,6 @@
     return found == strObj;
 }
 
-static int markStringObject(void* strObj, void* arg)
-{
-    UNUSED_PARAMETER(arg);
-    dvmMarkObjectNonNull(strObj);
-    return 0;
-}
-
-/*
- * Blacken string references from the literal string table.  The
- * literal table is a root.
- */
-void dvmGcScanInternedStrings()
-{
-    /* It's possible for a GC to happen before dvmStringInternStartup()
-     * is called.
-     */
-    if (gDvm.literalStrings != NULL) {
-        dvmHashForeach(gDvm.literalStrings, markStringObject, NULL);
-    }
-}
-
 /*
  * Clear white references from the intern table.
  */
diff --git a/vm/Jni.c b/vm/Jni.c
index a7d460f..f2ee2ae 100644
--- a/vm/Jni.c
+++ b/vm/Jni.c
@@ -1043,50 +1043,6 @@
     dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
 }
 
-/*
- * GC helper function to mark all JNI global references.
- *
- * We're currently handling the "pin" table here too.
- */
-void dvmGcMarkJniGlobalRefs()
-{
-    Object** op;
-
-    dvmLockMutex(&gDvm.jniGlobalRefLock);
-
-#ifdef USE_INDIRECT_REF
-    IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
-    op = pRefTable->table;
-    int numEntries = dvmIndirectRefTableEntries(pRefTable);
-    int i;
-
-    for (i = 0; i < numEntries; i++) {
-        Object* obj = *op;
-        if (obj != NULL)
-            dvmMarkObjectNonNull(obj);
-        op++;
-    }
-#else
-    op = gDvm.jniGlobalRefTable.table;
-    while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
-        dvmMarkObjectNonNull(*(op++));
-    }
-#endif
-
-    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
-
-
-    dvmLockMutex(&gDvm.jniPinRefLock);
-
-    op = gDvm.jniPinRefTable.table;
-    while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
-        dvmMarkObjectNonNull(*(op++));
-    }
-
-    dvmUnlockMutex(&gDvm.jniPinRefLock);
-}
-
-
 #ifndef USE_INDIRECT_REF
 /*
  * Determine if "obj" appears in the argument list for the native method.
diff --git a/vm/Thread.c b/vm/Thread.c
index c2e11c8..dd87483 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -3911,323 +3911,3 @@
     return NULL;
 }
 #endif /*WITH_MONITOR_TRACKING*/
-
-
-/*
- * GC helper functions
- */
-
-/*
- * Add the contents of the registers from the interpreted call stack.
- */
-static void gcScanInterpStackReferences(Thread *thread)
-{
-    const u4 *framePtr;
-#if WITH_EXTRA_GC_CHECKS > 1
-    bool first = true;
-#endif
-
-    framePtr = (const u4 *)thread->curFrame;
-    while (framePtr != NULL) {
-        const StackSaveArea *saveArea;
-        const Method *method;
-
-        saveArea = SAVEAREA_FROM_FP(framePtr);
-        method = saveArea->method;
-        if (method != NULL) {
-#ifdef COUNT_PRECISE_METHODS
-            /* the GC is running, so no lock required */
-            if (dvmPointerSetAddEntry(gDvm.preciseMethods, method))
-                LOGI("PGC: added %s.%s %p\n",
-                    method->clazz->descriptor, method->name, method);
-#endif
-#if WITH_EXTRA_GC_CHECKS > 1
-            /*
-             * May also want to enable the memset() in the "invokeMethod"
-             * goto target in the portable interpreter.  That sets the stack
-             * to a pattern that makes referring to uninitialized data
-             * very obvious.
-             */
-
-            if (first) {
-                /*
-                 * First frame, isn't native, check the "alternate" saved PC
-                 * as a sanity check.
-                 *
-                 * It seems like we could check the second frame if the first
-                 * is native, since the PCs should be the same.  It turns out
-                 * this doesn't always work.  The problem is that we could
-                 * have calls in the sequence:
-                 *   interp method #2
-                 *   native method
-                 *   interp method #1
-                 *
-                 * and then GC while in the native method after returning
-                 * from interp method #2.  The currentPc on the stack is
-                 * for interp method #1, but thread->currentPc2 is still
-                 * set for the last thing interp method #2 did.
-                 *
-                 * This can also happen in normal execution:
-                 * - sget-object on not-yet-loaded class
-                 * - class init updates currentPc2
-                 * - static field init is handled by parsing annotations;
-                 *   static String init requires creation of a String object,
-                 *   which can cause a GC
-                 *
-                 * Essentially, any pattern that involves executing
-                 * interpreted code and then causes an allocation without
-                 * executing instructions in the original method will hit
-                 * this.  These are rare enough that the test still has
-                 * some value.
-                 */
-                if (saveArea->xtra.currentPc != thread->currentPc2) {
-                    LOGW("PGC: savedPC(%p) != current PC(%p), %s.%s ins=%p\n",
-                        saveArea->xtra.currentPc, thread->currentPc2,
-                        method->clazz->descriptor, method->name, method->insns);
-                    if (saveArea->xtra.currentPc != NULL)
-                        LOGE("  pc inst = 0x%04x\n", *saveArea->xtra.currentPc);
-                    if (thread->currentPc2 != NULL)
-                        LOGE("  pc2 inst = 0x%04x\n", *thread->currentPc2);
-                    dvmDumpThread(thread, false);
-                }
-            } else {
-                /*
-                 * It's unusual, but not impossible, for a non-first frame
-                 * to be at something other than a method invocation.  For
-                 * example, if we do a new-instance on a nonexistent class,
-                 * we'll have a lot of class loader activity on the stack
-                 * above the frame with the "new" operation.  Could also
-                 * happen while we initialize a Throwable when an instruction
-                 * fails.
-                 *
-                 * So there's not much we can do here to verify the PC,
-                 * except to verify that it's a GC point.
-                 */
-            }
-            assert(saveArea->xtra.currentPc != NULL);
-#endif
-
-            const RegisterMap* pMap;
-            const u1* regVector;
-            int i;
-
-            Method* nonConstMethod = (Method*) method;  // quiet gcc
-            pMap = dvmGetExpandedRegisterMap(nonConstMethod);
-            if (pMap != NULL) {
-                /* found map, get registers for this address */
-                int addr = saveArea->xtra.currentPc - method->insns;
-                regVector = dvmRegisterMapGetLine(pMap, addr);
-                if (regVector == NULL) {
-                    LOGW("PGC: map but no entry for %s.%s addr=0x%04x\n",
-                        method->clazz->descriptor, method->name, addr);
-                } else {
-                    LOGV("PGC: found map for %s.%s 0x%04x (t=%d)\n",
-                        method->clazz->descriptor, method->name, addr,
-                        thread->threadId);
-                }
-            } else {
-                /*
-                 * No map found.  If precise GC is disabled this is
-                 * expected -- we don't create pointers to the map data even
-                 * if it's present -- but if it's enabled it means we're
-                 * unexpectedly falling back on a conservative scan, so it's
-                 * worth yelling a little.
-                 */
-                if (gDvm.preciseGc) {
-                    LOGVV("PGC: no map for %s.%s\n",
-                        method->clazz->descriptor, method->name);
-                }
-                regVector = NULL;
-            }
-
-            if (regVector == NULL) {
-                /* conservative scan */
-                for (i = method->registersSize - 1; i >= 0; i--) {
-                    u4 rval = *framePtr++;
-                    if (rval != 0 && (rval & 0x3) == 0) {
-                        dvmMarkIfObject((Object *)rval);
-                    }
-                }
-            } else {
-                /*
-                 * Precise scan.  v0 is at the lowest address on the
-                 * interpreted stack, and is the first bit in the register
-                 * vector, so we can walk through the register map and
-                 * memory in the same direction.
-                 *
-                 * A '1' bit indicates a live reference.
-                 */
-                u2 bits = 1 << 1;
-                for (i = method->registersSize - 1; i >= 0; i--) {
-                    u4 rval = *framePtr++;
-
-                    bits >>= 1;
-                    if (bits == 1) {
-                        /* set bit 9 so we can tell when we're empty */
-                        bits = *regVector++ | 0x0100;
-                        LOGVV("loaded bits: 0x%02x\n", bits & 0xff);
-                    }
-
-                    if (rval != 0 && (bits & 0x01) != 0) {
-                        /*
-                         * Non-null, register marked as live reference.  This
-                         * should always be a valid object.
-                         */
-#if WITH_EXTRA_GC_CHECKS > 0
-                        if ((rval & 0x3) != 0 ||
-                            !dvmIsValidObject((Object*) rval))
-                        {
-                            /* this is very bad */
-                            LOGE("PGC: invalid ref in reg %d: 0x%08x\n",
-                                method->registersSize-1 - i, rval);
-                            LOGE("PGC: %s.%s addr 0x%04x\n",
-                                method->clazz->descriptor, method->name,
-                                saveArea->xtra.currentPc - method->insns);
-                        } else
-#endif
-                        {
-                            dvmMarkObjectNonNull((Object *)rval);
-                        }
-                    } else {
-                        /*
-                         * Null or non-reference, do nothing at all.
-                         */
-#if WITH_EXTRA_GC_CHECKS > 1
-                        if (dvmIsValidObject((Object*) rval)) {
-                            /* this is normal, but we feel chatty */
-                            LOGD("PGC: ignoring valid ref in reg %d: 0x%08x\n",
-                                method->registersSize-1 - i, rval);
-                        }
-#endif
-                    }
-                }
-                dvmReleaseRegisterMapLine(pMap, regVector);
-            }
-        }
-
-#if WITH_EXTRA_GC_CHECKS > 1
-        first = false;
-#endif
-
-        /* Don't fall into an infinite loop if things get corrupted.
-         */
-        assert((uintptr_t)saveArea->prevFrame > (uintptr_t)framePtr ||
-               saveArea->prevFrame == NULL);
-        framePtr = saveArea->prevFrame;
-    }
-}
-
-static void gcScanReferenceTable(ReferenceTable *refTable)
-{
-    Object **op;
-
-    //TODO: these asserts are overkill; turn them off when things stablize.
-    assert(refTable != NULL);
-    assert(refTable->table != NULL);
-    assert(refTable->nextEntry != NULL);
-    assert((uintptr_t)refTable->nextEntry >= (uintptr_t)refTable->table);
-    assert(refTable->nextEntry - refTable->table <= refTable->maxEntries);
-
-    op = refTable->table;
-    while ((uintptr_t)op < (uintptr_t)refTable->nextEntry) {
-        dvmMarkObjectNonNull(*(op++));
-    }
-}
-
-#ifdef USE_INDIRECT_REF
-static void gcScanIndirectRefTable(IndirectRefTable* pRefTable)
-{
-    Object** op = pRefTable->table;
-    int numEntries = dvmIndirectRefTableEntries(pRefTable);
-    int i;
-
-    for (i = 0; i < numEntries; i++) {
-        Object* obj = *op;
-        if (obj != NULL)
-            dvmMarkObjectNonNull(obj);
-        op++;
-    }
-}
-#endif
-
-/*
- * Scan a Thread and mark any objects it references.
- */
-static void gcScanThread(Thread *thread)
-{
-    assert(thread != NULL);
-
-    /*
-     * The target thread must be suspended or in a state where it can't do
-     * any harm (e.g. in Object.wait()).  The only exception is the current
-     * thread, which will still be active and in the "running" state.
-     *
-     * It's possible to encounter a false-positive here because a thread
-     * transitioning to running from (say) vmwait or native will briefly
-     * set their status to running before switching to suspended.  This
-     * is highly unlikely, but does mean that we don't want to abort if
-     * the situation arises.
-     */
-    if (thread->status == THREAD_RUNNING && thread != dvmThreadSelf()) {
-        Thread* self = dvmThreadSelf();
-        LOGW("threadid=%d: Warning: GC scanning a running thread (%d)\n",
-            self->threadId, thread->threadId);
-        dvmDumpThread(thread, true);
-        LOGW("Found by:\n");
-        dvmDumpThread(self, false);
-
-        /* continue anyway */
-    }
-
-    dvmMarkObject(thread->threadObj);   // could be NULL, when constructing
-
-    dvmMarkObject(thread->exception);   // usually NULL
-    gcScanReferenceTable(&thread->internalLocalRefTable);
-
-#ifdef USE_INDIRECT_REF
-    gcScanIndirectRefTable(&thread->jniLocalRefTable);
-#else
-    gcScanReferenceTable(&thread->jniLocalRefTable);
-#endif
-
-    if (thread->jniMonitorRefTable.table != NULL) {
-        gcScanReferenceTable(&thread->jniMonitorRefTable);
-    }
-
-    gcScanInterpStackReferences(thread);
-}
-
-static void gcScanAllThreads()
-{
-    Thread *thread;
-
-    /* Lock the thread list so we can safely use the
-     * next/prev pointers.
-     */
-    dvmLockThreadList(dvmThreadSelf());
-
-    for (thread = gDvm.threadList; thread != NULL;
-            thread = thread->next)
-    {
-        /* We need to scan our own stack, so don't special-case
-         * the current thread.
-         */
-        gcScanThread(thread);
-    }
-
-    dvmUnlockThreadList();
-}
-
-void dvmGcScanRootThreadGroups()
-{
-    /* We scan the VM's list of threads instead of going
-     * through the actual ThreadGroups, but it should be
-     * equivalent.
-     *
-     * This assumes that the ThreadGroup class object is in
-     * the root set, which should always be true;  it's
-     * loaded by the built-in class loader, which is part
-     * of the root set.
-     */
-    gcScanAllThreads();
-}
diff --git a/vm/alloc/Copying.c b/vm/alloc/Copying.c
index 01c238a..0d72bfc 100644
--- a/vm/alloc/Copying.c
+++ b/vm/alloc/Copying.c
@@ -2343,11 +2343,6 @@
     /* do nothing */
 }
 
-void dvmMarkObjectNonNull(const Object *obj)
-{
-    assert(!"implemented");
-}
-
 void dvmMarkDirtyObjects(void)
 {
     assert(!"implemented");
diff --git a/vm/alloc/GC.h b/vm/alloc/GC.h
index c65c807..ce623ea 100644
--- a/vm/alloc/GC.h
+++ b/vm/alloc/GC.h
@@ -38,104 +38,10 @@
  */
 
 /*
- * Mark an object and schedule it to be scanned for
- * references to other objects.
- *
- * @param obj must be a valid object
- */
-void dvmMarkObjectNonNull(const Object *obj);
-
-/*
- * Mark an object and schedule it to be scanned for
- * references to other objects.
- *
- * @param obj must be a valid object or NULL
- */
-#define dvmMarkObject(obj) \
-    do { \
-        Object *DMO_obj_ = (Object *)(obj); \
-        if (DMO_obj_ != NULL) { \
-            dvmMarkObjectNonNull(DMO_obj_); \
-        } \
-    } while (false)
-
-/*
- * If obj points to a valid object, mark it and
- * schedule it to be scanned for references to other
- * objects.
- *
- * @param obj any pointer that may be an Object, or NULL
-TODO: check for alignment, too (would require knowledge of heap chunks)
- */
-#define dvmMarkIfObject(obj) \
-    do { \
-        Object *DMIO_obj_ = (Object *)(obj); \
-        if (DMIO_obj_ != NULL && dvmIsValidObject(DMIO_obj_)) { \
-            dvmMarkObjectNonNull(DMIO_obj_); \
-        } \
-    } while (false)
-
-/*
- * Functions that handle scanning various objects for references.
- */
-
-/*
- * Mark all class objects loaded by the root class loader;
- * most of these are the java.* classes.
- *
- * Currently implemented in Class.c.
- */
-void dvmGcScanRootClassLoader(void);
-
-/*
- * Mark all root ThreadGroup objects, guaranteeing that
- * all live Thread objects will eventually be scanned.
- *
- * NOTE: this is a misnomer, because the current implementation
- * actually only scans the internal list of VM threads, which
- * will mark all VM-reachable Thread objects.  Someone else
- * must scan the root class loader, which will mark java/lang/ThreadGroup.
- * The ThreadGroup class object has static members pointing to
- * the root ThreadGroups, and these will be marked as a side-effect
- * of marking the class object.
- *
- * Currently implemented in Thread.c.
- */
-void dvmGcScanRootThreadGroups(void);
-
-/*
- * Mark all interned string objects.
- *
- * Currently implemented in Intern.c.
- */
-void dvmGcScanInternedStrings(void);
-
-/*
  * Remove any unmarked interned string objects from the table.
  *
  * Currently implemented in Intern.c.
  */
 void dvmGcDetachDeadInternedStrings(int (*isUnmarkedObject)(void *));
 
-/*
- * Mark all primitive class objects.
- *
- * Currently implemented in Array.c.
- */
-void dvmGcScanPrimitiveClasses(void);
-
-/*
- * Mark all JNI global references.
- *
- * Currently implemented in JNI.c.
- */
-void dvmGcMarkJniGlobalRefs(void);
-
-/*
- * Mark all debugger references.
- *
- * Currently implemented in Debugger.c.
- */
-void dvmGcMarkDebuggerRefs(void);
-
 #endif  // _DALVIK_ALLOC_GC
diff --git a/vm/alloc/HeapTable.c b/vm/alloc/HeapTable.c
index e9f2729..72cf791 100644
--- a/vm/alloc/HeapTable.c
+++ b/vm/alloc/HeapTable.c
@@ -182,17 +182,3 @@
 
     return obj;
 }
-
-void dvmHeapMarkLargeTableRefs(LargeHeapRefTable *table)
-{
-    while (table != NULL) {
-        Object **ref, **lastRef;
-
-        ref = table->refs.table;
-        lastRef = table->refs.nextEntry;
-        while (ref < lastRef) {
-            dvmMarkObjectNonNull(*ref++);
-        }
-        table = table->next;
-    }
-}
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index ccbb663..750256c 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -29,15 +29,12 @@
 #define GC_LOG_TAG      LOG_TAG "-gc"
 
 #if LOG_NDEBUG
-#define LOGV_GC(...)    ((void)0)
 #define LOGD_GC(...)    ((void)0)
 #else
-#define LOGV_GC(...)    LOG(LOG_VERBOSE, GC_LOG_TAG, __VA_ARGS__)
 #define LOGD_GC(...)    LOG(LOG_DEBUG, GC_LOG_TAG, __VA_ARGS__)
 #endif
 
 #define LOGE_GC(...)    LOG(LOG_ERROR, GC_LOG_TAG, __VA_ARGS__)
-#define LOG_SCAN(...)   LOGV_GC("SCAN: " __VA_ARGS__)
 
 #define ALIGN_DOWN(x, n) ((size_t)(x) & -(n))
 #define ALIGN_UP(x, n) (((size_t)(x) + (n) - 1) & ~((n) - 1))
@@ -155,19 +152,21 @@
     }
 }
 
-/* If the object hasn't already been marked, mark it and
- * schedule it to be scanned for references.
- *
- * obj may not be NULL.  The macro dvmMarkObject() should
- * be used in situations where a reference may be NULL.
- *
- * This function may only be called when marking the root
- * set.  When recursing, use the internal markObject().
+/*
+ * Callback applied to root references during the initial root
+ * marking.  Visited roots are always marked but are only pushed on
+ * the mark stack if their address is below the finger.
  */
-void dvmMarkObjectNonNull(const Object *obj)
+static void rootMarkObjectVisitor(void *addr, RootType type, u4 thread, void *arg)
 {
-    assert(obj != NULL);
-    markObjectNonNull(obj, &gDvm.gcHeap->markContext, false);
+    Object *obj;
+
+    assert(addr != NULL);
+    assert(arg != NULL);
+    obj = *(Object **)addr;
+    if (obj != NULL) {
+        markObjectNonNull(obj, arg, false);
+    }
 }
 
 /* Mark the set of root objects.
@@ -199,45 +198,14 @@
 void dvmHeapMarkRootSet()
 {
     GcHeap *gcHeap = gDvm.gcHeap;
-
-    LOG_SCAN("immune objects");
     dvmMarkImmuneObjects(gcHeap->markContext.immuneLimit);
-
-    LOG_SCAN("root class loader\n");
-    dvmGcScanRootClassLoader();
-    LOG_SCAN("primitive classes\n");
-    dvmGcScanPrimitiveClasses();
-
-    LOG_SCAN("root thread groups\n");
-    dvmGcScanRootThreadGroups();
-
-    LOG_SCAN("interned strings\n");
-    dvmGcScanInternedStrings();
-
-    LOG_SCAN("JNI global refs\n");
-    dvmGcMarkJniGlobalRefs();
-
-    LOG_SCAN("pending reference operations\n");
-    dvmHeapMarkLargeTableRefs(gcHeap->referenceOperations);
-
-    LOG_SCAN("pending finalizations\n");
-    dvmHeapMarkLargeTableRefs(gcHeap->pendingFinalizationRefs);
-
-    LOG_SCAN("debugger refs\n");
-    dvmGcMarkDebuggerRefs();
-
-    /* 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
+    dvmVisitRoots(rootMarkObjectVisitor, &gcHeap->markContext);
 }
 
 /*
- * Callback applied to root references.  If the root location contains
- * a white reference it is pushed on the mark stack and grayed.
+ * Callback applied to root references during root remarking.  If the
+ * root location contains a white reference it is pushed on the mark
+ * stack and grayed.
  */
 static void markObjectVisitor(void *addr, RootType type, u4 thread, void *arg)
 {
@@ -262,16 +230,6 @@
 }
 
 /*
- * Nothing past this point is allowed to use dvmMarkObject() or
- * dvmMarkObjectNonNull(), which are for root-marking only.
- * Scanning/recursion must use markObject(), which takes the finger
- * into account.
- */
-#undef dvmMarkObject
-#define dvmMarkObject __dont_use_dvmMarkObject__
-#define dvmMarkObjectNonNull __dont_use_dvmMarkObjectNonNull__
-
-/*
  * Scans instance fields.
  */
 static void scanFields(const Object *obj, GcMarkContext *ctx)
@@ -751,8 +709,6 @@
      * left on the mark stack.
      */
     processMarkStack(ctx);
-
-    LOG_SCAN("done with marked objects\n");
 }
 
 void dvmHeapReScanMarkedObjects(void)
diff --git a/vm/alloc/Visit.c b/vm/alloc/Visit.c
index 697cc3f..a317955 100644
--- a/vm/alloc/Visit.c
+++ b/vm/alloc/Visit.c
@@ -97,7 +97,8 @@
 }
 
 /*
- * Visits all stack slots. TODO: visit native methods.
+ * Visits all stack slots except those belonging to native method
+ * arguments.
  */
 static void visitThreadStack(RootVisitor *visitor, Thread *thread, void *arg)
 {
@@ -154,6 +155,17 @@
                         /*
                          * Register is marked as live, it's a valid root.
                          */
+#if WITH_EXTRA_GC_CHECKS
+                        if (fp[i] != 0 && !dvmIsValidObject((Object *)fp[i])) {
+                            /* this is very bad */
+                            LOGE("PGC: invalid ref in reg %d: %#x",
+                                 method->registersSize - 1 - i, fp[i]);
+                            LOGE("PGC: %s.%s addr %#x",
+                                 method->clazz->descriptor, method->name,
+                                 saveArea->xtra.currentPc - method->insns);
+                            continue;
+                        }
+#endif
                         (*visitor)(&fp[i], threadId, ROOT_JAVA_FRAME, arg);
                     }
                 }
@@ -216,10 +228,15 @@
     if (gDvm.dbgRegistry != NULL) {
         visitHashTable(visitor, gDvm.dbgRegistry, ROOT_DEBUGGER, arg);
     }
-    visitHashTable(visitor, gDvm.internedStrings, ROOT_INTERNED_STRING, arg);
-    visitHashTable(visitor, gDvm.literalStrings, ROOT_INTERNED_STRING, arg);
+    if (gDvm.literalStrings != NULL) {
+        visitHashTable(visitor, gDvm.literalStrings, ROOT_INTERNED_STRING, arg);
+    }
+    dvmLockMutex(&gDvm.jniGlobalRefLock);
     visitReferenceTable(visitor, &gDvm.jniGlobalRefTable, 0, ROOT_JNI_GLOBAL, arg);
+    dvmUnlockMutex(&gDvm.jniGlobalRefLock);
+    dvmLockMutex(&gDvm.jniPinRefLock);
     visitReferenceTable(visitor, &gDvm.jniPinRefTable, 0, ROOT_VM_INTERNAL, arg);
+    dvmUnlockMutex(&gDvm.jniPinRefLock);
     visitLargeHeapRefTable(visitor, gDvm.gcHeap->referenceOperations, ROOT_REFERENCE_CLEANUP, arg);
     visitLargeHeapRefTable(visitor, gDvm.gcHeap->pendingFinalizationRefs, ROOT_FINALIZING, arg);
     visitThreads(visitor, arg);
diff --git a/vm/oo/Array.c b/vm/oo/Array.c
index 3b1c652..bc57a5a 100644
--- a/vm/oo/Array.c
+++ b/vm/oo/Array.c
@@ -799,16 +799,3 @@
     size += array->length * dvmArrayClassElementWidth(array->obj.clazz);
     return size;
 }
-
-/*
- * Add all primitive classes to the root set of objects.
-TODO: do these belong to the root class loader?
- */
-void dvmGcScanPrimitiveClasses()
-{
-    int i;
-
-    for (i = 0; i < PRIM_MAX; i++) {
-        dvmMarkObject((Object *)gDvm.primitiveClass[i]);    // may be NULL
-    }
-}
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
index c6b169f..6e16adc 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -4884,34 +4884,6 @@
 }
 #endif
 
-
-/*
- * Mark all classes associated with the built-in loader.
- */
-static int markClassObject(void *clazz, void *arg)
-{
-    UNUSED_PARAMETER(arg);
-
-    dvmMarkObjectNonNull((Object *)clazz);
-    return 0;
-}
-
-/*
- * The garbage collector calls this to mark the class objects for all
- * loaded classes.
- */
-void dvmGcScanRootClassLoader()
-{
-    /* dvmClassStartup() may not have been called before the first GC.
-     */
-    if (gDvm.loadedClasses != NULL) {
-        dvmHashTableLock(gDvm.loadedClasses);
-        dvmHashForeach(gDvm.loadedClasses, markClassObject, NULL);
-        dvmHashTableUnlock(gDvm.loadedClasses);
-    }
-}
-
-
 /*
  * ===========================================================================
  *      Method Prototypes and Descriptors