static bool createMarkStack(GcMarkStack *stack) { const Object **limit; size_t size; int fd; /* Create a stack big enough for the worst possible case, * where the heap is perfectly full of the smallest object. * TODO: be better about memory usage; use a smaller stack with * overflow detection and recovery. */ size = dvmHeapSourceGetIdealFootprint() / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD); size = ALIGN_UP_TO_PAGE_SIZE(size); fd = ashmem_create_region("dalvik-heap-markstack", size); if (fd < 0) { LOGE_GC("Could not create %d-byte ashmem mark stack\n", size); return false; } limit = (const Object **)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); close(fd); if (limit == MAP_FAILED) { LOGE_GC("Could not mmap %d-byte ashmem mark stack\n", size); return false; } memset(stack, 0, sizeof(*stack)); stack->limit = limit; stack->base = (const Object **)((uintptr_t)limit + size); stack->top = stack->base; return true; }
static bool createMarkStack(GcMarkStack *stack) { const Object **limit; const char *name; size_t size; /* Create a stack big enough for the worst possible case, * where the heap is perfectly full of the smallest object. * TODO: be better about memory usage; use a smaller stack with * overflow detection and recovery. */ size = dvmHeapSourceGetIdealFootprint() * sizeof(Object*) / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD); size = ALIGN_UP_TO_PAGE_SIZE(size); name = "dalvik-mark-stack"; limit = dvmAllocRegion(size, PROT_READ | PROT_WRITE, name); if (limit == NULL) { LOGE_GC("Could not mmap %zd-byte ashmem region '%s'", size, name); return false; } stack->limit = limit; stack->base = (const Object **)((uintptr_t)limit + size); stack->top = stack->base; return true; }
/* * Initializes the stack top and advises the mark stack pages as needed. */ static bool createMarkStack(GcMarkStack *stack) { assert(stack != NULL); size_t length = dvmHeapSourceGetIdealFootprint() * sizeof(Object*) / (sizeof(Object) + HEAP_SOURCE_CHUNK_OVERHEAD); madvise(stack->base, length, MADV_NORMAL); stack->top = stack->base; return true; }
/* 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; }
void dvmLogGcStats(size_t numFreed, size_t sizeFreed, size_t gcTimeMs) { size_t perHeapActualSize[HEAP_SOURCE_MAX_HEAP_COUNT], perHeapAllowedSize[HEAP_SOURCE_MAX_HEAP_COUNT], perHeapNumAllocated[HEAP_SOURCE_MAX_HEAP_COUNT], perHeapSizeAllocated[HEAP_SOURCE_MAX_HEAP_COUNT]; unsigned char eventBuf[1 + (1 + sizeof(long long)) * 4]; size_t actualSize, allowedSize, numAllocated, sizeAllocated; size_t softLimit = dvmHeapSourceGetIdealFootprint(); size_t nHeaps = dvmHeapSourceGetNumHeaps(); /* Enough to quiet down gcc for unitialized variable check */ perHeapActualSize[0] = perHeapAllowedSize[0] = perHeapNumAllocated[0] = perHeapSizeAllocated[0] = 0; actualSize = dvmHeapSourceGetValue(HS_FOOTPRINT, perHeapActualSize, HEAP_SOURCE_MAX_HEAP_COUNT); allowedSize = dvmHeapSourceGetValue(HS_ALLOWED_FOOTPRINT, perHeapAllowedSize, HEAP_SOURCE_MAX_HEAP_COUNT); numAllocated = dvmHeapSourceGetValue(HS_OBJECTS_ALLOCATED, perHeapNumAllocated, HEAP_SOURCE_MAX_HEAP_COUNT); sizeAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, perHeapSizeAllocated, HEAP_SOURCE_MAX_HEAP_COUNT); /* * Construct the the first 64-bit value to write to the log. * Global information: * * [63 ] Must be zero * [62-24] ASCII process identifier * [23-12] GC time in ms * [11- 0] Bytes freed * */ long long event0; event0 = 0LL << 63 | (long long)intToFloat12(gcTimeMs) << 12 | (long long)intToFloat12(sizeFreed); insertProcessName(&event0); /* * Aggregated heap stats: * * [63-62] 10 * [61-60] Reserved; must be zero * [59-48] Objects freed * [47-36] Actual size (current footprint) * [35-24] Allowed size (current hard max) * [23-12] Objects allocated * [11- 0] Bytes allocated */ long long event1; event1 = 2LL << 62 | (long long)intToFloat12(numFreed) << 48 | (long long)intToFloat12(actualSize) << 36 | (long long)intToFloat12(allowedSize) << 24 | (long long)intToFloat12(numAllocated) << 12 | (long long)intToFloat12(sizeAllocated); /* * Report the current state of the zygote heap(s). * * The active heap is always heap[0]. We can be in one of three states * at present: * * (1) Still in the zygote. Zygote using heap[0]. * (2) In the zygote, when the first child is started. We created a * new heap just before the first fork() call, so the original * "zygote heap" is now heap[1], and we have a small heap[0] for * anything we do from here on. * (3) In an app process. The app gets a new heap[0], and can also * see the two zygote heaps [1] and [2] (probably unwise to * assume any specific ordering). * * So if nHeaps == 1, we want the stats from heap[0]; else we want * the sum of the values from heap[1] to heap[nHeaps-1]. * * * Zygote heap stats (except for the soft limit, which belongs to the * active heap): * * [63-62] 11 * [61-60] Reserved; must be zero * [59-48] Soft Limit (for the active heap) * [47-36] Actual size (current footprint) * [35-24] Allowed size (current hard max) * [23-12] Objects allocated * [11- 0] Bytes allocated */ long long event2; size_t zActualSize, zAllowedSize, zNumAllocated, zSizeAllocated; int firstHeap = (nHeaps == 1) ? 0 : 1; size_t hh; zActualSize = zAllowedSize = zNumAllocated = zSizeAllocated = 0; for (hh = firstHeap; hh < nHeaps; hh++) { zActualSize += perHeapActualSize[hh]; zAllowedSize += perHeapAllowedSize[hh]; zNumAllocated += perHeapNumAllocated[hh]; zSizeAllocated += perHeapSizeAllocated[hh]; } event2 = 3LL << 62 | (long long)intToFloat12(softLimit) << 48 | (long long)intToFloat12(zActualSize) << 36 | (long long)intToFloat12(zAllowedSize) << 24 | (long long)intToFloat12(zNumAllocated) << 12 | (long long)intToFloat12(zSizeAllocated); /* * Report the current external allocation stats and the native heap * summary. * * [63-48] Reserved; must be zero (TODO: put new data in these slots) * [47-36] dlmalloc_footprint * [35-24] mallinfo: total allocated space * [23-12] External byte limit * [11- 0] External bytes allocated */ long long event3; size_t externalLimit, externalBytesAllocated; size_t uordblks, footprint; #if 0 /* * This adds 2-5msec to the GC cost on a DVT, or about 2-3% of the cost * of a GC, so it's not horribly expensive but it's not free either. */ extern size_t dlmalloc_footprint(void); struct mallinfo mi; //u8 start, end; //start = dvmGetRelativeTimeNsec(); mi = mallinfo(); uordblks = mi.uordblks; footprint = dlmalloc_footprint(); //end = dvmGetRelativeTimeNsec(); //LOGD("mallinfo+footprint took %dusec; used=%zd footprint=%zd\n", // (int)((end - start) / 1000), mi.uordblks, footprint); #else uordblks = footprint = 0; #endif externalLimit = dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0); externalBytesAllocated = dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0); event3 = (long long)intToFloat12(footprint) << 36 | (long long)intToFloat12(uordblks) << 24 | (long long)intToFloat12(externalLimit) << 12 | (long long)intToFloat12(externalBytesAllocated); /* Build the event data. * [ 0: 0] item count (4) * [ 1: 1] EVENT_TYPE_LONG * [ 2: 9] event0 * [10:10] EVENT_TYPE_LONG * [11:18] event1 * [19:19] EVENT_TYPE_LONG * [20:27] event2 * [28:28] EVENT_TYPE_LONG * [29:36] event2 */ unsigned char *c = eventBuf; *c++ = 4; *c++ = EVENT_TYPE_LONG; memcpy(c, &event0, sizeof(event0)); c += sizeof(event0); *c++ = EVENT_TYPE_LONG; memcpy(c, &event1, sizeof(event1)); c += sizeof(event1); *c++ = EVENT_TYPE_LONG; memcpy(c, &event2, sizeof(event2)); c += sizeof(event2); *c++ = EVENT_TYPE_LONG; memcpy(c, &event3, sizeof(event3)); (void) android_btWriteLog(EVENT_LOG_TAG_dvm_gc_info, EVENT_TYPE_LIST, eventBuf, sizeof(eventBuf)); }