Use the card table to scan the immune region of the heap.

Using the card table results in a dramatic cost reduction in scanning
time.  During a reboot, scanning each object in the zygote for
pointers to the application heap costs an average of 20ms of thread
time on a stingray with 8 megabyte zygote.  In comparison, scanning
dirty cards in the zygote costs 300us on average.

Change-Id: I1dba35646d509e6b1b4535e291a1eb6f66d7b218
diff --git a/vm/alloc/CardTable.c b/vm/alloc/CardTable.c
index a745c43..bbbb4ad 100644
--- a/vm/alloc/CardTable.c
+++ b/vm/alloc/CardTable.c
@@ -44,11 +44,7 @@
  * byte is equal to GC_DIRTY_CARD. See dvmCardTableStartup for details.
  */
 
-/*
- * Initializes the card table; must be called before any other
- * dvmCardTable*() functions.
- */
-bool dvmCardTableStartup(size_t heapMaximumSize)
+static bool allocCardTable(size_t heapMaximumSize)
 {
     size_t length;
     void *allocBase;
@@ -60,6 +56,7 @@
 
     /* Set up the card table */
     length = heapMaximumSize / GC_CARD_SIZE;
+    assert(length * GC_CARD_SIZE == heapMaximumSize);
     /* Allocate an extra 256 bytes to allow fixed low-byte of base */
     allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE,
                             "dalvik-card-table");
@@ -83,19 +80,109 @@
     return true;
 }
 
+static bool allocModUnionTable(size_t heapMaximumSize)
+{
+    size_t length = heapMaximumSize / GC_CARD_SIZE / HB_BITS_PER_WORD;
+    assert(length * GC_CARD_SIZE * HB_BITS_PER_WORD == heapMaximumSize);
+    int prot = PROT_READ | PROT_WRITE;
+    void *allocBase = dvmAllocRegion(length, prot, "dalvik-modunion-table");
+    if (allocBase == NULL) {
+        return false;
+    }
+    GcHeap *gcHeap = gDvm.gcHeap;
+    gcHeap->modUnionTableBase = (u1*)allocBase;
+    gcHeap->modUnionTableLength = length;
+    return true;
+}
+
+/*
+ * Initializes the card table; must be called before any other
+ * dvmCardTable*() functions.
+ */
+bool dvmCardTableStartup(size_t heapMaximumSize)
+{
+    return allocCardTable(heapMaximumSize) && allocModUnionTable(heapMaximumSize);
+}
+
+/*
+ * Releases storage for the card table and clears its globals.
+ */
+static void freeCardTable()
+{
+    if (gDvm.biasedCardTableBase == NULL) {
+        return;
+    }
+    gDvm.biasedCardTableBase = NULL;
+    munmap(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength);
+    gDvm.gcHeap->cardTableBase = NULL;
+    gDvm.gcHeap->cardTableLength = 0;
+}
+
+/*
+ * Releases storage for the mod union table and clears its globals.
+ */
+static void freeModUnionTable()
+{
+    if (gDvm.gcHeap->modUnionTableBase == NULL) {
+        return;
+    }
+    munmap(gDvm.gcHeap->modUnionTableBase, gDvm.gcHeap->modUnionTableLength);
+    gDvm.gcHeap->modUnionTableBase = NULL;
+    gDvm.gcHeap->modUnionTableLength = 0;
+}
+
 /*
  * Tears down the entire CardTable.
  */
 void dvmCardTableShutdown()
 {
-    gDvm.biasedCardTableBase = NULL;
-    munmap(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength);
+    freeCardTable();
+    freeModUnionTable();
+}
+
+/*
+ * Set a bit in the mod union table for each dirty byte in the card
+ * table.  Clears the corresponding byte in the card table.
+ */
+static void moveCardsToModUnion(u1 *base, u1 *limit)
+{
+    GcHeap *h = gDvm.gcHeap;
+    u1 *baseCard = dvmCardFromAddr(base);
+    u1 *limitCard = dvmCardFromAddr(limit);
+    u4 *bits = (u4*)h->modUnionTableBase;
+    u1 *heapBase = (u1*)dvmHeapSourceGetBase();
+    u1 *card;
+    for (card = baseCard; card < limitCard; ++card) {
+        if (*card == GC_CARD_CLEAN) {
+            continue;
+        }
+        u1 *addr = (u1*)dvmAddrFromCard(card);
+        u1 *biased = (u1*)((uintptr_t)addr - (uintptr_t)heapBase);
+        size_t offset = (uintptr_t)biased / GC_CARD_SIZE / HB_BITS_PER_WORD;
+        u4 bit = 1 << (((uintptr_t)biased / GC_CARD_SIZE) % HB_BITS_PER_WORD);
+        assert((u1*)&bits[offset] >= h->modUnionTableBase);
+        assert((u1*)&bits[offset] < h->modUnionTableBase+h->modUnionTableLength);
+        bits[offset] |= bit;
+        *card = GC_CARD_CLEAN;
+    }
 }
 
 void dvmClearCardTable(void)
 {
-    assert(gDvm.gcHeap->cardTableBase != NULL);
-    memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, gDvm.gcHeap->cardTableLength);
+    uintptr_t base[HEAP_SOURCE_MAX_HEAP_COUNT];
+    uintptr_t limit[HEAP_SOURCE_MAX_HEAP_COUNT];
+    size_t numHeaps = dvmHeapSourceGetNumHeaps();
+    dvmHeapSourceGetRegions(base, NULL, limit, numHeaps);
+    size_t i;
+    for (i = 0; i < numHeaps; ++i) {
+        if (i != 0) {
+            moveCardsToModUnion((u1*)base[i], (u1*)limit[i]);
+        } else {
+            u1 *baseCard = dvmCardFromAddr((u1*)base[i]);
+            u1 *limitCard = dvmCardFromAddr((u1*)limit[i]);
+            memset(baseCard, GC_CARD_CLEAN, limitCard - baseCard);
+        }
+    }
 }
 
 /*
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index 6438216..c0ef8fb 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -647,6 +647,7 @@
     /* Mark the set of objects that are strongly reachable from the roots.
      */
     LOGD_HEAP("Marking...");
+    dvmClearCardTable();
     dvmHeapMarkRootSet();
 
     /* dvmHeapScanMarkedObjects() will build the lists of known
@@ -662,7 +663,6 @@
          * heap to allow mutator threads to allocate from free space.
          */
         rootEnd = dvmGetRelativeTimeMsec();
-        dvmClearCardTable();
         dvmUnlockHeap();
         dvmResumeAllThreads(SUSPEND_FOR_GC);
     }
@@ -672,7 +672,7 @@
      * objects will also be marked.
      */
     LOGD_HEAP("Recursing...");
-    dvmHeapScanMarkedObjects();
+    dvmHeapScanMarkedObjects(spec->isPartial);
 
     if (spec->isConcurrent) {
         /*
diff --git a/vm/alloc/HeapBitmap.c b/vm/alloc/HeapBitmap.c
index 15ba15c..ffeff62 100644
--- a/vm/alloc/HeapBitmap.c
+++ b/vm/alloc/HeapBitmap.c
@@ -135,12 +135,16 @@
  * visited.
  */
 void dvmHeapBitmapScanWalk(HeapBitmap *bitmap,
+                           uintptr_t base, uintptr_t max,
                            BitmapScanCallback *callback, void *arg)
 {
     assert(bitmap != NULL);
     assert(bitmap->bits != NULL);
     assert(callback != NULL);
-    uintptr_t end = HB_OFFSET_TO_INDEX(bitmap->max - bitmap->base);
+    assert(base <= max);
+    assert(base >= bitmap->base);
+    assert(max <= bitmap->max);
+    uintptr_t end = HB_OFFSET_TO_INDEX(max - base);
     uintptr_t i;
     for (i = 0; i <= end; ++i) {
         unsigned long word = bitmap->bits[i];
diff --git a/vm/alloc/HeapBitmap.h b/vm/alloc/HeapBitmap.h
index d7936ae..fecc2a9 100644
--- a/vm/alloc/HeapBitmap.h
+++ b/vm/alloc/HeapBitmap.h
@@ -113,6 +113,7 @@
  * address.
  */
 void dvmHeapBitmapScanWalk(HeapBitmap *bitmap,
+                           uintptr_t base, uintptr_t max,
                            BitmapScanCallback *callback, void *arg);
 
 /*
diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h
index 119c417..d10a417 100644
--- a/vm/alloc/HeapInternal.h
+++ b/vm/alloc/HeapInternal.h
@@ -99,6 +99,10 @@
     u1*             cardTableBase;
     size_t          cardTableLength;
 
+    /* GC's modified union table. */
+    u1*             modUnionTableBase;
+    size_t          modUnionTableLength;
+
     /* Is the GC running?  Used to avoid recursive calls to GC.
      */
     bool            gcRunning;
diff --git a/vm/alloc/HeapSource.c b/vm/alloc/HeapSource.c
index 68d429f..197fb6c 100644
--- a/vm/alloc/HeapSource.c
+++ b/vm/alloc/HeapSource.c
@@ -607,6 +607,7 @@
          */
         LOGV("Splitting out new zygote heap\n");
         gDvm.newZygoteHeapAllocated = true;
+        dvmClearCardTable();
         return addNewHeap(hs);
     }
     return true;
@@ -694,7 +695,8 @@
     return total;
 }
 
-void dvmHeapSourceGetRegions(uintptr_t *base, uintptr_t *max, size_t numHeaps)
+void dvmHeapSourceGetRegions(uintptr_t *base, uintptr_t *max, uintptr_t *limit,
+                             size_t numHeaps)
 {
     HeapSource *hs = gHs;
     size_t i;
@@ -704,7 +706,12 @@
     assert(numHeaps <= hs->numHeaps);
     for (i = 0; i < numHeaps; ++i) {
         base[i] = (uintptr_t)hs->heaps[i].base;
-        max[i] = MIN((uintptr_t)hs->heaps[i].limit - 1, hs->markBits.max);
+        if (max != NULL) {
+            max[i] = MIN((uintptr_t)hs->heaps[i].limit - 1, hs->markBits.max);
+        }
+        if (limit != NULL) {
+            limit[i] = (uintptr_t)hs->heaps[i].limit;
+        }
     }
 }
 
diff --git a/vm/alloc/HeapSource.h b/vm/alloc/HeapSource.h
index 61a2ce4..be6d2e5 100644
--- a/vm/alloc/HeapSource.h
+++ b/vm/alloc/HeapSource.h
@@ -68,7 +68,8 @@
  * heaps.  The base and max values are suitable for passing directly
  * to the bitmap sweeping routine.
  */
-void dvmHeapSourceGetRegions(uintptr_t *base, uintptr_t *max, size_t numHeaps);
+void dvmHeapSourceGetRegions(uintptr_t *base, uintptr_t *max, uintptr_t *limit,
+                             size_t numHeaps);
 
 /*
  * Get the bitmap representing all live objects.
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index ced52be..c925225 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -22,6 +22,7 @@
 #include "alloc/HeapSource.h"
 #include "alloc/MarkSweep.h"
 #include "alloc/Visit.h"
+#include "alloc/VisitInlines.h"
 #include <limits.h>     // for ULONG_MAX
 #include <sys/mman.h>   // for madvise(), mmap()
 #include <errno.h>
@@ -158,6 +159,170 @@
     }
 }
 
+/*
+ * Visits all objects that start on the given card.
+ */
+static void visitCard(Visitor *visitor, u1 *card, void *arg)
+{
+    assert(visitor != NULL);
+    assert(card != NULL);
+    assert(dvmIsValidCard(card));
+    u1 *addr= (u1*)dvmAddrFromCard(card);
+    u1 *limit = addr + GC_CARD_SIZE;
+    for (; addr < limit; addr += HB_OBJECT_ALIGNMENT) {
+        Object *obj = (Object *)addr;
+        GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+        if (isMarked(obj, ctx)) {
+            (*visitor)(obj, arg);
+        }
+    }
+}
+
+/*
+ * Visits objects on dirty cards marked the mod union table.
+ */
+static void visitModUnionTable(Visitor *visitor, u1 *base, u1 *limit, void *arg)
+{
+    assert(visitor != NULL);
+    assert(base != NULL);
+    assert(limit != NULL);
+    assert(base <= limit);
+    u1 *heapBase = (u1*)dvmHeapSourceGetBase();
+    /* compute the start address in the bit table */
+    assert(base >= heapBase);
+    u4 *bits = (u4*)gDvm.gcHeap->modUnionTableBase;
+    /* compute the end address in the bit table */
+    size_t length = (limit - base) / GC_CARD_SIZE;
+    assert(length % sizeof(*bits) == 0);
+    length /= 4;
+    size_t i;
+    for (i = 0; i < length; ++i) {
+        if (bits[i] == 0) {
+            continue;
+        }
+        u4 word = bits[i];
+        bits[i] = 0;
+        size_t j = 0;
+        for (j = 0; j < sizeof(u4)*CHAR_BIT; ++j) {
+            if (word & (1 << j)) {
+                /* compute the base of the card */
+                size_t offset = (i*sizeof(u4)*CHAR_BIT + j) * GC_CARD_SIZE;
+                u1* addr = heapBase + offset;
+                u1* card = dvmCardFromAddr(addr);
+                /* visit all objects on the card */
+                visitCard(visitor, card, arg);
+            }
+        }
+    }
+}
+
+/*
+ * Visits objects on dirty cards marked in the card table.
+ */
+static void visitCardTable(Visitor *visitor, u1 *base, u1 *limit, void *arg)
+{
+    assert(visitor != NULL);
+    assert(base != NULL);
+    assert(limit != NULL);
+    u1 *start = dvmCardFromAddr(base);
+    u1 *end = dvmCardFromAddr(limit);
+    while (start < end) {
+        u1 *dirty = (u1 *)memchr(start, GC_CARD_DIRTY, end - start);
+        if (dirty == NULL) {
+            break;
+        }
+        assert(dirty >= start);
+        assert(dirty <= end);
+        assert(dvmIsValidCard(dirty));
+        visitCard(visitor, dirty, arg);
+        start = dirty + 1;
+    }
+}
+
+typedef struct {
+    Object *threatenBoundary;
+    Object *currObject;
+} ScanImmuneObjectContext;
+
+/*
+ * Marks the referent of an immune object it is threatened.
+ */
+static void scanImmuneObjectReferent(void *addr, void *arg)
+{
+    assert(addr != NULL);
+    assert(arg != NULL);
+    Object *obj = *(Object **)addr;
+    ScanImmuneObjectContext *ctx = (ScanImmuneObjectContext *)arg;
+    if (obj == NULL) {
+        return;
+    }
+    if (obj >= ctx->threatenBoundary) {
+        /* TODO: set a bit in the mod union table instead. */
+        dvmMarkCard(ctx->currObject);
+        markObjectNonNull(obj, &gDvm.gcHeap->markContext, false);
+   }
+}
+
+/*
+ * This function is poorly named, as is its callee.
+ */
+static void scanImmuneObject(void *addr, void *arg)
+{
+    ScanImmuneObjectContext *ctx = (ScanImmuneObjectContext *)arg;
+    Object *obj = (Object *)addr;
+    ctx->currObject = obj;
+    visitObject(scanImmuneObjectReferent, obj, arg);
+}
+
+/*
+ * Verifies that immune objects have their referents marked.
+ */
+static void verifyImmuneObjectsVisitor(void *addr, void *arg)
+{
+    assert(addr != NULL);
+    assert(arg != NULL);
+    Object *obj = *(Object **)addr;
+    GcMarkContext *ctx = (GcMarkContext *)arg;
+    if (obj == NULL || obj < (Object *)ctx->immuneLimit) {
+        return;
+    }
+    assert(dvmIsValidObject(obj));
+    if (!isMarked(obj, ctx)) {
+        LOGE("Immune reference %p points to a white threatened object %p",
+             addr, obj);
+        dvmAbort();
+    }
+}
+
+/*
+ * Visitor that searches for immune objects and verifies that all
+ * threatened referents are marked.
+ */
+static void verifyImmuneObjectsCallback(void *addr, void *arg)
+{
+    assert(addr != NULL);
+    assert(arg != NULL);
+    Object *obj = (Object *)addr;
+    GcMarkContext *ctx = (GcMarkContext *)arg;
+    if (obj->clazz == NULL) {
+        LOGI("uninitialized object @ %p (has null clazz pointer)", obj);
+        return;
+    }
+    if (obj < (Object *)ctx->immuneLimit) {
+        visitObject(verifyImmuneObjectsVisitor, obj, ctx);
+    }
+}
+
+/*
+ * Verify that immune objects refer to marked objects.
+ */
+static void verifyImmuneObjects()
+{
+    const HeapBitmap *bitmap = dvmHeapSourceGetLiveBits();
+    GcMarkContext *ctx = &gDvm.gcHeap->markContext;
+    dvmHeapBitmapWalk(bitmap, verifyImmuneObjectsCallback, ctx);
+}
+
 /* Mark the set of root objects.
  *
  * Things we need to scan:
@@ -184,6 +349,10 @@
  * - Native stack (for in-progress stuff in the VM)
  *   - The TrackedAlloc stuff watches all native VM references.
  */
+
+/*
+ * Blackens the root set.
+ */
 void dvmHeapMarkRootSet()
 {
     GcHeap *gcHeap = gDvm.gcHeap;
@@ -470,6 +639,7 @@
     assert(ctx != NULL);
     assert(isMarked(obj, ctx));
     assert(obj->clazz != NULL);
+    assert(isMarked(obj, ctx));
     if (obj->clazz == gDvm.classJavaLangClass) {
         scanClassObject(obj, ctx);
     } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
@@ -497,101 +667,31 @@
     }
 }
 
-static size_t objectSize(const Object *obj)
-{
-    assert(dvmIsValidObject(obj));
-    assert(dvmIsValidObject((Object *)obj->clazz));
-    if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISARRAY)) {
-        return dvmArrayObjectSize((ArrayObject *)obj);
-    } else if (obj->clazz == gDvm.classJavaLangClass) {
-        return dvmClassObjectSize((ClassObject *)obj);
-    } else {
-        return obj->clazz->objectSize;
-    }
-}
-
-/*
- * Scans forward to the header of the next marked object between start
- * and limit.  Returns NULL if no marked objects are in that region.
- */
-static Object *nextGrayObject(const u1 *base, const u1 *limit,
-                              const HeapBitmap *markBits)
-{
-    const u1 *ptr;
-
-    assert(base < limit);
-    assert(limit - base <= GC_CARD_SIZE);
-    for (ptr = base; ptr < limit; ptr += HB_OBJECT_ALIGNMENT) {
-        if (dvmHeapBitmapIsObjectBitSet(markBits, ptr))
-            return (Object *)ptr;
-    }
-    return NULL;
-}
-
-/*
- * Scans range of dirty cards between start and end.  A range of dirty
- * cards is composed consecutively dirty cards or dirty cards spanned
- * by a gray object.  Returns the address of a clean card if the scan
- * reached a clean card or NULL if the scan reached the end.
- */
-const u1 *scanDirtyCards(const u1 *start, const u1 *end,
-                         GcMarkContext *ctx)
-{
-    const HeapBitmap *markBits = ctx->bitmap;
-    const u1 *card = start, *prevAddr = NULL;
-    while (card < end) {
-        if (*card != GC_CARD_DIRTY) {
-            return card;
-        }
-        const u1 *ptr = prevAddr ? prevAddr : (u1*)dvmAddrFromCard(card);
-        const u1 *limit = ptr + GC_CARD_SIZE;
-        while (ptr < limit) {
-            Object *obj = nextGrayObject(ptr, limit, markBits);
-            if (obj == NULL) {
-                break;
-            }
-            scanObject(obj, ctx);
-            ptr = (u1*)obj + ALIGN_UP(objectSize(obj), HB_OBJECT_ALIGNMENT);
-        }
-        if (ptr < limit) {
-            /* Ended within the current card, advance to the next card. */
-            ++card;
-            prevAddr = NULL;
-        } else {
-            /* Ended past the current card, skip ahead. */
-            card = dvmCardFromAddr(ptr);
-            prevAddr = ptr;
-        }
-    }
-    return NULL;
-}
-
 /*
  * Blackens gray objects found on dirty cards.
  */
 static void scanGrayObjects(GcMarkContext *ctx)
 {
-    GcHeap *h = gDvm.gcHeap;
-    const u1 *base, *limit, *ptr, *dirty;
-    size_t footprint;
+    HeapBitmap *bitmap = ctx->bitmap;
+    u1 *base = (u1 *)bitmap->base;
+    u1 *limit = (u1 *)ALIGN_UP(bitmap->max, GC_CARD_SIZE);
+    visitCardTable((Visitor *)scanObject, base, limit, ctx);
+}
 
-    footprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
-    base = &h->cardTableBase[0];
-    limit = dvmCardFromAddr((u1 *)dvmHeapSourceGetBase() + footprint);
-    assert(limit <= &h->cardTableBase[h->cardTableLength]);
-
-    ptr = base;
-    for (;;) {
-        dirty = (const u1 *)memchr(ptr, GC_CARD_DIRTY, limit - ptr);
-        if (dirty == NULL) {
-            break;
-        }
-        assert((dirty > ptr) && (dirty < limit));
-        ptr = scanDirtyCards(dirty, limit, ctx);
-        if (ptr == NULL) {
-            break;
-        }
-        assert((ptr > dirty) && (ptr < limit));
+/*
+ * Iterate through the immune objects and mark their referents.  Uses
+ * the mod union table to save scanning time.
+ */
+void dvmHeapScanImmuneObjects(const GcMarkContext *ctx)
+{
+    ScanImmuneObjectContext ctx2;
+    memset(&ctx2, 0, sizeof(ctx2));
+    ctx2.threatenBoundary = (Object*)ctx->immuneLimit;
+    visitModUnionTable(scanImmuneObject,
+                       (u1*)ctx->bitmap->base, (u1*)ctx->immuneLimit,
+                       (void *)&ctx2);
+    if (gDvm.verifyCardTable) {
+        verifyImmuneObjects();
     }
 }
 
@@ -611,22 +711,32 @@
  * reachable objects.  When this returns, the entire set of
  * live objects will be marked and the mark stack will be empty.
  */
-void dvmHeapScanMarkedObjects(void)
+void dvmHeapScanMarkedObjects(bool isPartial)
 {
     GcMarkContext *ctx = &gDvm.gcHeap->markContext;
 
+    assert(ctx != NULL);
     assert(ctx->finger == NULL);
 
-    /* The bitmaps currently have bits set for the root set.
-     * Walk across the bitmaps and scan each object.
+    u1 *start;
+    if (isPartial && dvmHeapSourceGetNumHeaps() > 1) {
+        dvmHeapScanImmuneObjects(ctx);
+        start = (u1 *)ctx->immuneLimit;
+    } else {
+        start = (u1*)ctx->bitmap->base;
+    }
+    /*
+     * All objects reachable from the root set have a bit set in the
+     * mark bitmap.  Walk the mark bitmap and blacken these objects.
      */
-    dvmHeapBitmapScanWalk(ctx->bitmap, scanBitmapCallback, ctx);
+    dvmHeapBitmapScanWalk(ctx->bitmap,
+                          (uintptr_t)start, ctx->bitmap->max,
+                          scanBitmapCallback,
+                          ctx);
 
     ctx->finger = (void *)ULONG_MAX;
 
-    /* We've walked the mark bitmaps.  Scan anything that's
-     * left on the mark stack.
-     */
+    /* Process gray objects until the mark stack it is empty. */
     processMarkStack(ctx);
 }
 
@@ -832,7 +942,7 @@
         totalPendCount += newPendCount;
         finRefs = finRefs->next;
     }
-    LOGD("scheduleFinalizations(): %zd finalizers triggered.", totalPendCount);
+    LOGV("scheduleFinalizations(): %zd finalizers triggered.", totalPendCount);
     if (totalPendCount == 0) {
         /* No objects required finalization.
          * Free the empty temporary table.
@@ -1003,7 +1113,7 @@
     size_t i;
 
     numHeaps = dvmHeapSourceGetNumHeaps();
-    dvmHeapSourceGetRegions(base, max, numHeaps);
+    dvmHeapSourceGetRegions(base, max, NULL, numHeaps);
     if (isPartial) {
         assert((uintptr_t)gDvm.gcHeap->markContext.immuneLimit == base[0]);
         numSweepHeaps = 1;
diff --git a/vm/alloc/MarkSweep.h b/vm/alloc/MarkSweep.h
index 9b57f45..0672aa8 100644
--- a/vm/alloc/MarkSweep.h
+++ b/vm/alloc/MarkSweep.h
@@ -49,7 +49,7 @@
 bool dvmHeapBeginMarkStep(bool isPartial);
 void dvmHeapMarkRootSet(void);
 void dvmHeapReMarkRootSet(void);
-void dvmHeapScanMarkedObjects(void);
+void dvmHeapScanMarkedObjects(bool isPartial);
 void dvmHeapReScanMarkedObjects(void);
 void dvmHeapProcessReferences(Object **softReferences, bool clearSoftRefs,
                               Object **weakReferences,