*/
pthread_mutex_t gcHeapLock;
+ /*
+ * Condition variable to queue threads waiting to retry an
+ * allocation. Signaled after a concurrent GC is completed.
+ */
+ pthread_cond_t gcHeapCond;
+
/* Opaque pointer representing the heap. */
GcHeap* gcHeap;
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);
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);
}
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
#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) {
}
}
+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.
*/
void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason);
+/*
+ * Blocks the until the GC thread signals the completion of a
+ * concurrent GC.
+ */
+void dvmWaitForConcurrentGcToComplete(void);
+
#endif // _DALVIK_ALLOC_HEAP
*
* These aren't exact, and should not be treated as such.
*/
-static inline void
-countAllocation(Heap *heap, const void *ptr, bool isObj)
+static void countAllocation(Heap *heap, const void *ptr, bool isObj)
{
HeapSource *hs;
HeapSource *hs = gHs;
Heap *heap;
void *ptr;
+ size_t allocated;
HS_BOILERPLATE();
heap = hs2heap(hs);
-
- if (heap->bytesAllocated + n <= hs->softLimit) {
- ptr = mspace_calloc(heap->msp, 1, n);
- if (ptr != NULL) {
- countAllocation(heap, ptr, true);
- size_t allocated = heap->bytesAllocated - heap->prevBytesAllocated;
- if (allocated > OCCUPANCY_THRESHOLD) {
- if (hs->hasGcThread == true) {
- dvmSignalCond(&gHs->gcThreadCond);
- }
- }
- }
- } else {
- /* This allocation would push us over the soft limit;
- * act as if the heap is full.
+ if (heap->bytesAllocated + n > hs->softLimit) {
+ /*
+ * This allocation would push us over the soft limit; act as
+ * if the heap is full.
*/
LOGV_HEAP("softLimit of %zd.%03zdMB hit for %zd-byte allocation\n",
- FRACTIONAL_MB(hs->softLimit), n);
- ptr = NULL;
+ FRACTIONAL_MB(hs->softLimit), n);
+ return NULL;
+ }
+ ptr = mspace_calloc(heap->msp, 1, n);
+ if (ptr == NULL) {
+ return NULL;
+ }
+ countAllocation(heap, ptr, true);
+ /*
+ * Check to see if a concurrent GC should be initiated.
+ */
+ if (gDvm.gcHeap->gcRunning || !hs->hasGcThread) {
+ /*
+ * The garbage collector thread is already running or has yet
+ * to be started. Do nothing.
+ */
+ return ptr;
+ }
+ allocated = heap->bytesAllocated - heap->prevBytesAllocated;
+ if (allocated > OCCUPANCY_THRESHOLD) {
+ /*
+ * We have exceeded the occupancy threshold. Wake up the
+ * garbage collector.
+ */
+ dvmSignalCond(&gHs->gcThreadCond);
}
return ptr;
}
goto out;
}
+ if (gDvm.gcHeap->gcRunning) {
+ /*
+ * The GC is concurrently tracing the heap. Release the heap
+ * lock, wait for the GC to complete, and try again.
+ */
+ dvmWaitForConcurrentGcToComplete();
+ if (externalAlloc(hs, n, false)) {
+ ret = true;
+ goto out;
+ }
+ }
+
/* The "allocation" failed. Free up some space by doing
* a full garbage collection. This may grow the heap source
* if the live set is sufficiently large.