Allow allocation during a concurrent GC.
Previously, any thread performing a GC held the heap lock for the
entire GC. If the GC performed was a concurrent GC, mutator threads
that allocate during the GC would be blocked until the GC completed.
With this change, if the GC performed is a concurrent GC, the heap
lock is released while the roots are being traced. If a mutator
thread allocates an object from available storage, the allocation
proceeds. If a mutator thread attempts to allocate an object larger
than available storage, the thread will block until the GC completes.
Change-Id: I91a04179c6f583f878b685405a6fdd16b9995017
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index 80b5030..cc3fd72 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -285,9 +285,25 @@
return ptr;
}
- /* The allocation failed. Free up some space by doing
- * a full garbage collection. This may grow the heap
- * if the live set is sufficiently large.
+ /*
+ * The allocation failed. If the GC is running, block until it
+ * completes and retry.
+ */
+ if (gDvm.gcHeap->gcRunning) {
+ /*
+ * The GC is concurrently tracing the heap. Release the heap
+ * lock, wait for the GC to complete, and retrying allocating.
+ */
+ dvmWaitForConcurrentGcToComplete();
+ ptr = dvmHeapSourceAlloc(size);
+ if (ptr != NULL) {
+ return ptr;
+ }
+ }
+ /*
+ * Another failure. Our thread was starved or there may be too
+ * many live objects. Try a foreground GC. This will have no
+ * effect if the concurrent GC is already running.
*/
gcForMalloc(false);
ptr = dvmHeapSourceAlloc(size);
@@ -758,9 +774,10 @@
if (reason == GC_CONCURRENT) {
/*
- * We are performing a concurrent collection. Resume all
- * threads for the duration of the recursive mark.
+ * Resume threads while tracing from the roots. We unlock the
+ * heap to allow mutator threads to allocate from free space.
*/
+ dvmUnlockHeap();
dvmResumeAllThreads(SUSPEND_FOR_GC);
}
@@ -773,9 +790,10 @@
if (reason == GC_CONCURRENT) {
/*
- * We are performing a concurrent collection. Perform the
- * final thread suspension.
+ * Re-acquire the heap lock and perform the final thread
+ * suspension.
*/
+ dvmLockHeap();
dvmSuspendAllThreads(SUSPEND_FOR_GC);
/*
* As no barrier intercepts root updates, we conservatively
@@ -892,6 +910,15 @@
#endif
dvmResumeAllThreads(SUSPEND_FOR_GC);
+
+ if (reason == GC_CONCURRENT) {
+ /*
+ * Wake-up any threads that blocked after a failed allocation
+ * request.
+ */
+ dvmBroadcastCond(&gDvm.gcHeapCond);
+ }
+
if (reason != GC_CONCURRENT) {
if (oldThreadPriority != kInvalidPriority) {
if (setpriority(PRIO_PROCESS, 0, oldThreadPriority) != 0) {
@@ -925,6 +952,16 @@
}
}
+void dvmWaitForConcurrentGcToComplete(void)
+{
+ Thread *self = dvmThreadSelf();
+ ThreadStatus oldStatus;
+ assert(self != NULL);
+ oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+ dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
+ dvmChangeStatus(self, oldStatus);
+}
+
#if WITH_HPROF
/*
* Perform garbage collection, writing heap information to the specified file.