/* * Allocates <n> bytes of zeroed data, growing as much as possible * if necessary. */ void* dvmHeapSourceAllocAndGrow(size_t n) { HS_BOILERPLATE(); HeapSource *hs = gHs; Heap* heap = hs2heap(hs); void* ptr = dvmHeapSourceAlloc(n); if (ptr != NULL) { return ptr; } size_t oldIdealSize = hs->idealSize; if (isSoftLimited(hs)) { /* We're soft-limited. Try removing the soft limit to * see if we can allocate without actually growing. */ hs->softLimit = SIZE_MAX; ptr = dvmHeapSourceAlloc(n); if (ptr != NULL) { /* Removing the soft limit worked; fix things up to * reflect the new effective ideal size. */ snapIdealFootprint(); return ptr; } // softLimit intentionally left at SIZE_MAX. } /* We're not soft-limited. Grow the heap to satisfy the request. * If this call fails, no footprints will have changed. */ ptr = heapAllocAndGrow(hs, heap, n); if (ptr != NULL) { /* The allocation succeeded. Fix up the ideal size to * reflect any footprint modifications that had to happen. */ snapIdealFootprint(); } else { /* We just couldn't do it. Restore the original ideal size, * fixing up softLimit if necessary. */ setIdealFootprint(oldIdealSize); } return ptr; }
/* Remove any hard limits, try to allocate, and shrink back down. * Last resort when trying to allocate an object. */ static void* heapAllocAndGrow(HeapSource *hs, Heap *heap, size_t n) { /* Grow as much as possible, but don't let the real footprint * go over the absolute max. */ size_t max = heap->maximumSize; mspace_set_footprint_limit(heap->msp, max); void* ptr = dvmHeapSourceAlloc(n); /* Shrink back down as small as possible. Our caller may * readjust max_allowed to a more appropriate value. */ mspace_set_footprint_limit(heap->msp, mspace_footprint(heap->msp)); return ptr; }
/* Try as hard as possible to allocate some memory. */ static void *tryMalloc(size_t size) { void *ptr; /* Don't try too hard if there's no way the allocation is * going to succeed. We have to collect SoftReferences before * throwing an OOME, though. */ if (size >= gDvm.heapGrowthLimit) { ALOGW("%zd byte allocation exceeds the %zd byte maximum heap size", size, gDvm.heapGrowthLimit); ptr = NULL; goto collect_soft_refs; } //TODO: figure out better heuristics // There will be a lot of churn if someone allocates a bunch of // big objects in a row, and we hit the frag case each time. // A full GC for each. // Maybe we grow the heap in bigger leaps // Maybe we skip the GC if the size is large and we did one recently // (number of allocations ago) (watch for thread effects) // DeflateTest allocs a bunch of ~128k buffers w/in 0-5 allocs of each other // (or, at least, there are only 0-5 objects swept each time) ptr = dvmHeapSourceAlloc(size); if (ptr != NULL) { return ptr; } /* * 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 (ptr != NULL) { return ptr; } /* Even that didn't work; this is an exceptional state. * Try harder, growing the heap if necessary. */ ptr = dvmHeapSourceAllocAndGrow(size); if (ptr != NULL) { size_t newHeapSize; newHeapSize = dvmHeapSourceGetIdealFootprint(); //TODO: may want to grow a little bit more so that the amount of free // space is equal to the old free space + the utilization slop for // the new allocation. LOGI_HEAP("Grow heap (frag case) to " "%zu.%03zuMB for %zu-byte allocation", FRACTIONAL_MB(newHeapSize), size); return ptr; } /* Most allocations should have succeeded by now, so the heap * is really full, really fragmented, or the requested size is * really big. Do another GC, collecting SoftReferences this * time. The VM spec requires that all SoftReferences have * been collected and cleared before throwing an OOME. */ //TODO: wait for the finalizers from the previous GC to finish collect_soft_refs: LOGI_HEAP("Forcing collection of SoftReferences for %zu-byte allocation", size); gcForMalloc(true); ptr = dvmHeapSourceAllocAndGrow(size); if (ptr != NULL) { return ptr; } //TODO: maybe wait for finalizers and try one last time LOGE_HEAP("Out of memory on a %zd-byte allocation.", size); //TODO: tell the HeapSource to dump its state dvmDumpThread(dvmThreadSelf(), false); return NULL; }