* GC option flags.
*/
bool preciseGc;
- bool overwriteFree;
bool preVerify;
bool postVerify;
bool generateRegisterMaps;
dvmFprintf(stderr, " -Xdeadlockpredict:{off,warn,err,abort}\n");
dvmFprintf(stderr, " -Xstacktracefile:<filename>\n");
dvmFprintf(stderr, " -Xgc:[no]precise\n");
- dvmFprintf(stderr, " -Xgc:[no]overwritefree\n");
dvmFprintf(stderr, " -Xgc:[no]preverify\n");
dvmFprintf(stderr, " -Xgc:[no]postverify\n");
dvmFprintf(stderr, " -Xgc:[no]concurrent\n");
gDvm.preciseGc = true;
else if (strcmp(argv[i] + 5, "noprecise") == 0)
gDvm.preciseGc = false;
- else if (strcmp(argv[i] + 5, "overwritefree") == 0)
- gDvm.overwriteFree = true;
- else if (strcmp(argv[i] + 5, "nooverwritefree") == 0)
- gDvm.overwriteFree = false;
else if (strcmp(argv[i] + 5, "preverify") == 0)
gDvm.preVerify = true;
else if (strcmp(argv[i] + 5, "nopreverify") == 0)
return lookupInternedString(strObj, true);
}
+/*
+ * Returns true if the object is a weak interned string. Any string
+ * interned by the user is weak.
+ */
+bool dvmIsWeakInternedString(const StringObject* strObj)
+{
+ StringObject* found;
+ u4 hash;
+
+ assert(strObj != NULL);
+ if (gDvm.internedStrings == NULL) {
+ return false;
+ }
+ dvmLockMutex(&gDvm.internLock);
+ hash = dvmComputeStringHash(strObj);
+ found = dvmHashTableLookup(gDvm.internedStrings, hash, (void*)strObj,
+ dvmHashcmpStrings, false);
+ dvmUnlockMutex(&gDvm.internLock);
+ return found == strObj;
+}
+
static int markStringObject(void* strObj, void* arg)
{
UNUSED_PARAMETER(arg);
bool dvmStringInternStartup(void);
void dvmStringInternShutdown(void);
-
StringObject* dvmLookupInternedString(StringObject* strObj);
StringObject* dvmLookupImmortalInternedString(StringObject* strObj);
+bool dvmIsWeakInternedString(const StringObject* strObj);
#endif /*_DALVIK_INTERN*/
return hash;
}
-u4 dvmComputeStringHash(StringObject* strObj) {
+u4 dvmComputeStringHash(const StringObject* strObj) {
ArrayObject* chars = (ArrayObject*) dvmGetFieldObject((Object*) strObj,
STRING_FIELDOFF_VALUE);
int offset, len;
/*
* Hash function for string objects.
*/
-u4 dvmComputeStringHash(StringObject* strObj);
+u4 dvmComputeStringHash(const StringObject* strObj);
/*
* Create a java/lang/String from a C string.
* limitations under the License.
*/
-/*
- * Needed for PROT_* definitions.
- */
-#include <sys/mman.h>
+#include <sys/mman.h> /* for PROT_* */
#include "Dalvik.h"
#include "alloc/HeapSource.h"
u1 *cardAddr = dvmCardFromAddr(addr);
*cardAddr = GC_CARD_DIRTY;
}
+
+/*
+ * Handles the complexity of object arrays for isObjectDirty. Array
+ * objects are exactly marked so all spanned cards are examined.
+ */
+static bool isObjectArrayDirty(const Object *obj)
+{
+ u1 *ptr, *limit;
+ size_t size;
+
+ assert(obj != NULL);
+ assert(dvmIsValidObject(obj));
+ assert(IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISOBJECTARRAY));
+ size = dvmArrayObjectSize((const ArrayObject *)obj);
+ ptr = dvmCardFromAddr(obj);
+ limit = dvmCardFromAddr((u1 *)obj + size - 1) + 1;
+ assert(ptr != limit);
+ for (; ptr != limit; ++ptr) {
+ if (*ptr == GC_CARD_DIRTY) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Returns true if the object is on a dirty card.
+ */
+static bool isObjectDirty(const Object *obj)
+{
+ assert(obj != NULL);
+ assert(dvmIsValidObject(obj));
+ if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISOBJECTARRAY)) {
+ return isObjectArrayDirty(obj);
+ } else {
+ u1 *card = dvmCardFromAddr(obj);
+ return *card == GC_CARD_DIRTY;
+ }
+}
+
+/*
+ * Context structure for verifying the card table.
+ */
+typedef struct {
+ HeapBitmap *markBits;
+ size_t whiteRefs;
+} WhiteReferenceCounter;
+
+/*
+ * Visitor that counts white referents.
+ */
+static void countWhiteReferenceVisitor(void *addr, void *arg)
+{
+ WhiteReferenceCounter *ctx;
+ Object *obj;
+
+ assert(addr != NULL);
+ assert(arg != NULL);
+ obj = *(Object **)addr;
+ assert(dvmIsValidObject(obj));
+ ctx = arg;
+ if (obj == NULL || dvmHeapBitmapIsObjectBitSet(ctx->markBits, obj)) {
+ return;
+ }
+ ctx->whiteRefs += 1;
+}
+
+/*
+ * Returns true if the given object is a reference object and the
+ * just the referent is unmarked.
+ */
+static bool isReferentUnmarked(const Object *obj,
+ const WhiteReferenceCounter* ctx)
+{
+ assert(obj != NULL);
+ assert(obj->clazz != NULL);
+ assert(ctx != NULL);
+ if (ctx->whiteRefs != 1) {
+ return false;
+ } else if (IS_CLASS_FLAG_SET(obj->clazz, CLASS_ISREFERENCE)) {
+ size_t offset = gDvm.offJavaLangRefReference_referent;
+ const Object *referent = dvmGetFieldObject(obj, offset);
+ return !dvmHeapBitmapIsObjectBitSet(ctx->markBits, referent);
+ } else {
+ return false;
+ }
+}
+
+/*
+ * Returns true if the given object is a string and has been interned
+ * by the user.
+ */
+static bool isWeakInternedString(const Object *obj)
+{
+ assert(obj != NULL);
+ if (obj->clazz == gDvm.classJavaLangString) {
+ return dvmIsWeakInternedString((StringObject *)obj);
+ } else {
+ return false;
+ }
+}
+
+/*
+ * Callback applied to marked objects. If the object is found to be
+ * gray a message is written to the log. By virtue of where the card
+ * table verification occurs weak references have yet to be blackened
+ * and so their containing objects are permitted to be gray.
+ */
+static void verifyCardTableCallback(size_t numPtrs, void **ptrs,
+ const void *finger, void *arg)
+{
+ size_t i;
+
+ for (i = 0; i < numPtrs; ++i) {
+ Object *obj = ptrs[i];
+ WhiteReferenceCounter ctx = { arg, 0 };
+ dvmVisitObject(countWhiteReferenceVisitor, obj, &ctx);
+ if (ctx.whiteRefs == 0) {
+ continue;
+ } else if (isObjectDirty(obj)) {
+ continue;
+ } else if (isReferentUnmarked(obj, &ctx)) {
+ continue;
+ } else if (isWeakInternedString(obj)) {
+ continue;
+ }
+ LOGE("Verify failed, object %p is gray", obj);
+ dvmDumpObject(obj);
+ dvmAbort();
+ }
+}
+
+/*
+ * Verifies that gray objects are on a dirty card.
+ */
+void dvmVerifyCardTable(void)
+{
+ HeapBitmap *markBits = gDvm.gcHeap->markContext.bitmap;
+ dvmHeapBitmapWalk(markBits, verifyCardTableCallback, markBits);
+}
#ifndef _DALVIK_ALLOC_CARDTABLE
#define _DALVIK_ALLOC_CARDTABLE
-/*
- * TODO: Better documentation of these values pending integration of
- * concurrent collections and the card table.
- */
#define GC_CARD_SHIFT 7
#define GC_CARD_SIZE (1 << GC_CARD_SHIFT)
#define GC_CARD_CLEAN 0
void dvmMarkCard(const void *addr);
/*
- * dvmAbort if any clean object in the Zygote heap contains a
- * reference to the application heap, or if the immune limit is not as
- * expected.
+ * Verifies that all gray objects are on a dirty card.
*/
void dvmVerifyCardTable(void);
-/* TODO: Clearing, querying, and iterating over the card table. */
-
#endif /*_DALVIK_ALLOC_CARDTABLE*/
return dvmHeapSourceChunkSize(obj);
}
-/*
- * Scan every live object in the heap, holding the locks.
- */
-static void verifyHeap(void)
+static void verifyRootsAndHeap(void)
{
dvmVerifyRoots();
dvmVerifyBitmap(dvmHeapSourceGetLiveBits());
dvmLockMutex(&gDvm.heapWorkerListLock);
if (gDvm.preVerify) {
- LOGV_HEAP("Verifying heap before GC");
- verifyHeap();
+ LOGV_HEAP("Verifying roots and heap before GC");
+ verifyRootsAndHeap();
}
#ifdef WITH_PROFILER
*/
dvmHeapReMarkRootSet();
/*
+ * With the exception of reference objects and weak interned
+ * strings, all gray objects should now be on dirty cards.
+ */
+ if (gDvm.verifyCardTable) {
+ dvmVerifyCardTable();
+ }
+ /*
* Recursively mark gray objects pointed to by the roots or by
* heap objects dirtied during the concurrent mark.
*/
LOGV_HEAP("GC finished");
if (gDvm.postVerify) {
- LOGV_HEAP("Verifying heap after GC");
- verifyHeap();
+ LOGV_HEAP("Verifying roots and heap after GC");
+ verifyRootsAndHeap();
}
gcHeap->gcRunning = false;
{
size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT];
- /* The heap must be locked before the HeapWorker;
- * unroll and re-order the locks. dvmLockHeap()
- * will put us in VMWAIT if necessary. Once it
- * returns, there shouldn't be any contention on
- * heapWorkerLock.
+ /*
+ * 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();
+ while (gDvm.gcHeap->gcRunning) {
+ dvmWaitForConcurrentGcToComplete();
+ }
dvmLockMutex(&gDvm.heapWorkerLock);
memset(madvisedSizes, 0, sizeof(madvisedSizes));
dvmWaitCond(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
}
- /* dvmChangeStatus() may block; don't hold 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();
+ while (gDvm.gcHeap->gcRunning) {
+ dvmWaitForConcurrentGcToComplete();
+ }
dvmLockMutex(&gDvm.heapWorkerLock);
+ dvmUnlockHeap();
LOGV("HeapWorker is awake\n");
/* Process any events in the queue.
ArmLIR *target = newLIR0(cUnit, kArmPseudoTargetLabel);
target->defMask = ENCODE_ALL;
branchOver->generic.target = (LIR *)target;
+ dvmCompilerFreeTemp(cUnit, regCardBase);
+ dvmCompilerFreeTemp(cUnit, regCardNo);
}
static bool genConversionCall(CompilationUnit *cUnit, MIR *mir, void *funct,
scale, kWord);
HEAP_ACCESS_SHADOW(false);
+ dvmCompilerFreeTemp(cUnit, regPtr);
+ dvmCompilerFreeTemp(cUnit, regIndex);
+
/* NOTE: marking card here based on object head */
markCard(cUnit, r0, r1);
}
/* NOTE: marking card based on field address */
markCard(cUnit, rlSrc.lowReg, tReg);
}
+ dvmCompilerFreeTemp(cUnit, tReg);
break;
}