Replace the scanObject routines with one derived from Verify.
It seems to be slightly faster this way, as well as more readable.
Change-Id: I92b9c54289c484cb4606f054f258da4a4bef8658
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index 41fc2ac..ecd663c 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -25,17 +25,6 @@
#include <cutils/ashmem.h>
#include <errno.h>
-#define GC_DEBUG_PARANOID 2
-#define GC_DEBUG_BASIC 1
-#define GC_DEBUG_OFF 0
-#define GC_DEBUG(l) (GC_DEBUG_LEVEL >= (l))
-
-#if 1
-#define GC_DEBUG_LEVEL GC_DEBUG_PARANOID
-#else
-#define GC_DEBUG_LEVEL GC_DEBUG_OFF
-#endif
-
#define VERBOSE_GC 0
#define GC_LOG_TAG LOG_TAG "-gc"
@@ -75,8 +64,6 @@
* may be > 1<<8.
*/
static inline long isMarked(const void *obj, const GcMarkContext *ctx)
- __attribute__((always_inline));
-static inline long isMarked(const void *obj, const GcMarkContext *ctx)
{
return dvmHeapBitmapIsObjectBitSet(ctx->bitmap, obj);
}
@@ -146,27 +133,19 @@
return true;
}
-static long setAndReturnMarkBit(GcMarkContext *ctx, const void *obj)
- __attribute__((always_inline));
static long
setAndReturnMarkBit(GcMarkContext *ctx, const void *obj)
{
return dvmHeapBitmapSetAndReturnObjectBit(ctx->bitmap, obj);
}
-static void _markObjectNonNullCommon(const Object *obj, GcMarkContext *ctx,
- bool checkFinger, bool forceStack)
- __attribute__((always_inline));
static void
-_markObjectNonNullCommon(const Object *obj, GcMarkContext *ctx,
+markObjectNonNull(const Object *obj, GcMarkContext *ctx,
bool checkFinger, bool forceStack)
{
+ assert(ctx != NULL);
assert(obj != NULL);
-
-#if GC_DEBUG(GC_DEBUG_PARANOID)
-//TODO: make sure we're locked
assert(dvmIsValidObject(obj));
-#endif
if (obj < ctx->immuneLimit) {
assert(isMarked(obj, ctx));
@@ -206,20 +185,13 @@
* the finger won't be visited by the bitmap scan, so those objects
* need to be added to the mark stack.
*/
-static void
-markObjectNonNull(const Object *obj, GcMarkContext *ctx)
+static void markObject(const Object *obj, GcMarkContext *ctx)
{
- _markObjectNonNullCommon(obj, ctx, true, false);
+ if (obj != NULL) {
+ markObjectNonNull(obj, ctx, true, false);
+ }
}
-#define markObject(obj, ctx) \
- do { \
- Object *MO_obj_ = (Object *)(obj); \
- if (MO_obj_ != NULL) { \
- markObjectNonNull(MO_obj_, (ctx)); \
- } \
- } while (false)
-
/* If the object hasn't already been marked, mark it and
* schedule it to be scanned for references.
*
@@ -227,12 +199,13 @@
* 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[NonNull]().
+ * set. When recursing, use the internal markObject().
*/
void
dvmMarkObjectNonNull(const Object *obj)
{
- _markObjectNonNullCommon(obj, &gDvm.gcHeap->markContext, false, false);
+ assert(obj != NULL);
+ markObjectNonNull(obj, &gDvm.gcHeap->markContext, false, false);
}
/* Mark the set of root objects.
@@ -335,269 +308,226 @@
}
/*
- * Nothing past this point is allowed to use dvmMarkObject*().
- * Scanning/recursion must use markObject*(), which takes the
- * finger into account.
+ * 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__
-
-/* Mark all of a ClassObject's interfaces.
+/*
+ * Scans instance fields.
*/
-static void markInterfaces(const ClassObject *clazz, GcMarkContext *ctx)
+static void scanInstanceFields(const Object *obj, GcMarkContext *ctx)
{
- ClassObject **interfaces;
- int interfaceCount;
- int i;
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ assert(ctx != NULL);
- /* Mark all interfaces.
- */
- interfaces = clazz->interfaces;
- interfaceCount = clazz->interfaceCount;
- for (i = 0; i < interfaceCount; i++) {
- markObjectNonNull((Object *)*interfaces, ctx);
- interfaces++;
- }
-}
-
-/* Mark all objects referred to by a ClassObject's static fields.
- */
-static void scanStaticFields(const ClassObject *clazz, GcMarkContext *ctx)
-{
- const StaticField *f;
- int i;
-
- //TODO: Optimize this with a bit vector or something
- f = &clazz->sfields[0];
- for (i = 0; i < clazz->sfieldCount; i++) {
- char c = f->field.signature[0];
- if (c == '[' || c == 'L') {
- /* It's an array or class reference.
- */
- markObject((Object *)f->value.l, ctx);
- }
- f++;
- }
-}
-
-/* Mark all objects referred to by a DataObject's instance fields.
- */
-static void scanInstanceFields(const DataObject *obj, ClassObject *clazz,
- GcMarkContext *ctx)
-{
- if (clazz->refOffsets != CLASS_WALK_SUPER) {
- unsigned int refOffsets = clazz->refOffsets;
+ if (obj->clazz->refOffsets != CLASS_WALK_SUPER) {
+ unsigned int refOffsets = obj->clazz->refOffsets;
while (refOffsets != 0) {
const int rshift = CLZ(refOffsets);
refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
markObject(dvmGetFieldObject((Object*)obj,
- CLASS_OFFSET_FROM_CLZ(rshift)), ctx);
+ CLASS_OFFSET_FROM_CLZ(rshift)), ctx);
}
} else {
- while (clazz != NULL) {
- InstField *f;
- int i;
-
- /* All of the fields that contain object references
- * are guaranteed to be at the beginning of the ifields list.
- */
- f = clazz->ifields;
- for (i = 0; i < clazz->ifieldRefCount; i++) {
- /* Mark the array or object reference.
- * May be NULL.
- *
- * Note that, per the comment on struct InstField,
- * f->byteOffset is the offset from the beginning of
- * obj, not the offset into obj->instanceData.
- */
- markObject(dvmGetFieldObject((Object*)obj, f->byteOffset), ctx);
- f++;
+ ClassObject *clazz;
+ int i;
+ for (clazz = obj->clazz; clazz != NULL; clazz = clazz->super) {
+ InstField *field = clazz->ifields;
+ for (i = 0; i < clazz->ifieldRefCount; ++i, ++field) {
+ void *addr = BYTE_OFFSET((Object *)obj, field->byteOffset);
+ markObject(((JValue *)addr)->l, ctx);
}
-
- /* This will be NULL when we hit java.lang.Object
- */
- clazz = clazz->super;
}
}
}
-/* Mark all objects referred to by the array's contents.
+/*
+ * Scans the header, static field references, and interface
+ * pointers of a class object.
*/
-static void scanObjectArray(const ArrayObject *array, GcMarkContext *ctx)
+static void scanClassObject(const ClassObject *obj, GcMarkContext *ctx)
{
- Object **contents;
- u4 length;
- u4 i;
+ ClassObject *clazz;
+ int i;
- contents = (Object **)array->contents;
- length = array->length;
+ assert(obj != NULL);
+ assert(obj->obj.clazz == gDvm.classJavaLangClass);
+ assert(ctx != NULL);
- for (i = 0; i < length; i++) {
- markObject(*contents, ctx); // may be NULL
- contents++;
+ markObject((Object *)obj->obj.clazz, ctx);
+ if (IS_CLASS_FLAG_SET(obj, CLASS_ISARRAY)) {
+ markObject((Object *)obj->elementClass, ctx);
+ }
+ markObject((Object *)obj->super, ctx);
+ markObject(obj->classLoader, ctx);
+ /* Scan static field references. */
+ for (i = 0; i < obj->sfieldCount; ++i) {
+ char ch = obj->sfields[i].field.signature[0];
+ if (ch == '[' || ch == 'L') {
+ markObject(obj->sfields[i].value.l, ctx);
+ }
+ }
+ /* Scan the instance fields. */
+ scanInstanceFields((const Object *)obj, ctx);
+ /* Scan interface references. */
+ for (i = 0; i < obj->interfaceCount; ++i) {
+ markObject((Object *)obj->interfaces[i], ctx);
}
}
-/* Mark all objects referred to by the ClassObject.
+/*
+ * Scans the header of all array objects. If the array object is
+ * specialized to a reference type, scans the array data as well.
*/
-static void scanClassObject(const ClassObject *clazz, GcMarkContext *ctx)
+static void scanArrayObject(const ArrayObject *obj, GcMarkContext *ctx)
{
- LOGV_SCAN("---------> %s\n", clazz->name);
+ size_t i;
- if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
- /* We're an array; mark the class object of the contents
- * of the array.
- *
- * Note that we won't necessarily reach the array's element
- * class by scanning the array contents; the array may be
- * zero-length, or may only contain null objects.
- */
- markObjectNonNull((Object *)clazz->elementClass, ctx);
+ assert(obj != NULL);
+ assert(obj->obj.clazz != NULL);
+ assert(ctx != NULL);
+ /* Scan the class object reference. */
+ markObject((Object *)obj->obj.clazz, ctx);
+ if (IS_CLASS_FLAG_SET(obj->obj.clazz, CLASS_ISOBJECTARRAY)) {
+ /* Scan the array contents. */
+ Object **contents = (Object **)obj->contents;
+ for (i = 0; i < obj->length; ++i) {
+ markObject(contents[i], ctx);
+ }
}
+}
- /* We scan these explicitly in case the only remaining
- * reference to a particular class object is via a data
- * object; we may not be guaranteed to reach all
- * live class objects via a classloader.
+/*
+ * Process the "referent" field in a java.lang.ref.Reference. If the
+ * referent has not yet been marked, put it on the appropriate list in
+ * the gcHeap for later processing.
+ */
+static void delayReferenceReferent(const DataObject *obj,
+ GcMarkContext *ctx)
+{
+ assert(obj != NULL);
+ assert(obj->obj.clazz != NULL);
+ assert(ctx != NULL);
+
+ GcHeap *gcHeap = gDvm.gcHeap;
+ Object *referent;
+
+ /* It's a subclass of java/lang/ref/Reference.
+ * The fields in this class have been arranged
+ * such that scanInstanceFields() did not actually
+ * mark the "referent" field; we need to handle
+ * it specially.
+ *
+ * If the referent already has a strong mark (isMarked(referent)),
+ * we don't care about its reference status.
*/
- markObject((Object *)clazz->super, ctx); // may be NULL (java.lang.Object)
- markObject(clazz->classLoader, ctx); // may be NULL
+ referent = dvmGetFieldObject((Object *)obj,
+ gDvm.offJavaLangRefReference_referent);
+ if (referent != NULL && !isMarked(referent, ctx))
+ {
+ u4 refFlags;
- scanStaticFields(clazz, ctx);
- markInterfaces(clazz, ctx);
+ /* Find out what kind of reference is pointing
+ * to referent.
+ */
+ refFlags = GET_CLASS_FLAG_GROUP(obj->obj.clazz,
+ CLASS_ISREFERENCE |
+ CLASS_ISWEAKREFERENCE |
+ CLASS_ISPHANTOMREFERENCE);
+
+ /* We use the vmData field of Reference objects
+ * as a next pointer in a singly-linked list.
+ * That way, we don't need to allocate any memory
+ * while we're doing a GC.
+ */
+#define ADD_REF_TO_LIST(list, ref) \
+ do { \
+ Object *ARTL_ref_ = (/*de-const*/Object *)(ref); \
+ dvmSetFieldObject(ARTL_ref_, \
+ gDvm.offJavaLangRefReference_vmData, list); \
+ list = ARTL_ref_; \
+ } while (false)
+
+ /* At this stage, we just keep track of all of
+ * the live references that we've seen. Later,
+ * we'll walk through each of these lists and
+ * deal with the referents.
+ */
+ if (refFlags == CLASS_ISREFERENCE) {
+ /* It's a soft reference. Depending on the state,
+ * we'll attempt to collect all of them, some of
+ * them, or none of them.
+ */
+ ADD_REF_TO_LIST(gcHeap->softReferences, obj);
+ } else {
+ /* It's a weak or phantom reference.
+ * Clearing CLASS_ISREFERENCE will reveal which.
+ */
+ refFlags &= ~CLASS_ISREFERENCE;
+ if (refFlags == CLASS_ISWEAKREFERENCE) {
+ ADD_REF_TO_LIST(gcHeap->weakReferences, obj);
+ } else if (refFlags == CLASS_ISPHANTOMREFERENCE) {
+ ADD_REF_TO_LIST(gcHeap->phantomReferences, obj);
+ } else {
+ assert(!"Unknown reference type");
+ }
+ }
+#undef ADD_REF_TO_LIST
+ }
}
-/* Mark all objects that obj refers to.
- *
- * Called on every object in markList.
+/*
+ * Scans the header and field references of a data object.
+ */
+static void scanDataObject(const DataObject *obj, GcMarkContext *ctx)
+{
+ assert(obj != NULL);
+ assert(obj->obj.clazz != NULL);
+ assert(ctx != NULL);
+ /* Scan the class object. */
+ markObject((Object *)obj->obj.clazz, ctx);
+ /* Scan the instance fields. */
+ scanInstanceFields((const Object *)obj, ctx);
+
+ if (IS_CLASS_FLAG_SET(obj->obj.clazz, CLASS_ISREFERENCE)) {
+ delayReferenceReferent(obj, ctx);
+ }
+}
+
+/*
+ * Scans an object reference. Determines the type of the reference
+ * and dispatches to a specialized scanning routine.
*/
static void scanObject(const Object *obj, GcMarkContext *ctx)
{
ClassObject *clazz;
- assert(dvmIsValidObject(obj));
- LOGV_SCAN("0x%08x %s\n", (uint)obj, obj->clazz->descriptor);
-
-#if WITH_HPROF
- if (gDvm.gcHeap->hprofContext != NULL) {
- hprofDumpHeapObject(gDvm.gcHeap->hprofContext, obj);
- }
-#endif
-
- /* Get and mark the class object for this particular instance.
- */
+ assert(obj != NULL);
+ assert(ctx != NULL);
+ /* Check that the object is aligned. */
+ assert(((uintptr_t)obj & 7) == 0);
clazz = obj->clazz;
- if (clazz == NULL) {
- /* This can happen if we catch an object between dvmMalloc()
- * and DVM_OBJECT_INIT(). The object won't contain any
- * references yet, so we can just skip it. It can also mean
- * that this object is unlinkedJavaLangClass, which has its
- * clazz explicitly set to NULL.
- */
+ /* Check that the class object is aligned. */
+ assert(((uintptr_t)clazz & 7) == 0);
+ /* Dispatch a type-specific scan routine. */
+ if (clazz == gDvm.classJavaLangClass) {
+ scanClassObject((ClassObject *)obj, ctx);
+ } else if (clazz == NULL) {
return;
} else if (clazz == gDvm.unlinkedJavaLangClass) {
- /* This class hasn't been linked yet. We're guaranteed
- * that the object doesn't contain any references that
- * aren't already tracked, so we can skip scanning it.
- */
return;
- }
-
- assert(dvmIsValidObject((Object *)clazz));
- markObjectNonNull((Object *)clazz, ctx);
-
- /* Mark any references in this object.
- */
- if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
- /* It's an array object.
- */
- if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
- /* It's an array of object references.
- */
- scanObjectArray((ArrayObject *)obj, ctx);
- }
- // else there's nothing else to scan
} else {
- /* It's a DataObject-compatible object.
- */
- scanInstanceFields((DataObject *)obj, clazz, ctx);
-
- if (IS_CLASS_FLAG_SET(clazz, CLASS_ISREFERENCE)) {
- GcHeap *gcHeap = gDvm.gcHeap;
- Object *referent;
-
- /* It's a subclass of java/lang/ref/Reference.
- * The fields in this class have been arranged
- * such that scanInstanceFields() did not actually
- * mark the "referent" field; we need to handle
- * it specially.
- *
- * If the referent already has a strong mark (isMarked(referent)),
- * we don't care about its reference status.
- */
- referent = dvmGetFieldObject(obj,
- gDvm.offJavaLangRefReference_referent);
- if (referent != NULL && !isMarked(referent, ctx))
- {
- u4 refFlags;
-
- /* Find out what kind of reference is pointing
- * to referent.
- */
- refFlags = GET_CLASS_FLAG_GROUP(clazz,
- CLASS_ISREFERENCE |
- CLASS_ISWEAKREFERENCE |
- CLASS_ISPHANTOMREFERENCE);
-
- /* We use the vmData field of Reference objects
- * as a next pointer in a singly-linked list.
- * That way, we don't need to allocate any memory
- * while we're doing a GC.
- */
-#define ADD_REF_TO_LIST(list, ref) \
- do { \
- Object *ARTL_ref_ = (/*de-const*/Object *)(ref); \
- dvmSetFieldObject(ARTL_ref_, \
- gDvm.offJavaLangRefReference_vmData, list); \
- list = ARTL_ref_; \
- } while (false)
-
- /* At this stage, we just keep track of all of
- * the live references that we've seen. Later,
- * we'll walk through each of these lists and
- * deal with the referents.
- */
- if (refFlags == CLASS_ISREFERENCE) {
- /* It's a soft reference. Depending on the state,
- * we'll attempt to collect all of them, some of
- * them, or none of them.
- */
- ADD_REF_TO_LIST(gcHeap->softReferences, obj);
- } else {
- /* It's a weak or phantom reference.
- * Clearing CLASS_ISREFERENCE will reveal which.
- */
- refFlags &= ~CLASS_ISREFERENCE;
- if (refFlags == CLASS_ISWEAKREFERENCE) {
- ADD_REF_TO_LIST(gcHeap->weakReferences, obj);
- } else if (refFlags == CLASS_ISPHANTOMREFERENCE) {
- ADD_REF_TO_LIST(gcHeap->phantomReferences, obj);
- } else {
- assert(!"Unknown reference type");
- }
- }
-#undef ADD_REF_TO_LIST
- }
- }
-
- /* If this is a class object, mark various other things that
- * its internals point to.
- *
- * All class objects are instances of java.lang.Class,
- * including the java.lang.Class class object.
- */
- if (clazz == gDvm.classJavaLangClass) {
- scanClassObject((ClassObject *)obj, ctx);
+ assert(clazz != NULL);
+ if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+ scanArrayObject((ArrayObject *)obj, ctx);
+ } else {
+ scanDataObject((DataObject *)obj, ctx);
}
}
}
@@ -763,7 +693,8 @@
marked = isMarked(referent, markContext);
if (!marked && ((++counter) & 1)) {
/* Referent is white and biased toward saving, mark it. */
- markObjectNonNull(referent, markContext);
+ assert(referent != NULL);
+ markObject(referent, markContext);
marked = true;
}
if (marked) {
@@ -926,7 +857,8 @@
assert(ref < lastRef);
HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_FINALIZING, 0);
while (ref < lastRef) {
- markObjectNonNull(*ref, markContext);
+ assert(*ref != NULL);
+ markObject(*ref, markContext);
ref++;
}
HPROF_CLEAR_GC_SCAN_STATE();