diff options
author | Andy McFadden <fadden@android.com> | 2011-11-04 12:56:55 -0700 |
---|---|---|
committer | Andy McFadden <fadden@android.com> | 2011-11-04 13:06:04 -0700 |
commit | af6cf54652d1b27885b99e216bee29b955052630 (patch) | |
tree | 914acd163e35c069034aae38f01759fd7681c7c2 | |
parent | ab97cd1634ed4e2c9e5c558a9f88ed9ca8623c8c (diff) | |
download | android_dalvik-af6cf54652d1b27885b99e216bee29b955052630.tar.gz android_dalvik-af6cf54652d1b27885b99e216bee29b955052630.tar.bz2 android_dalvik-af6cf54652d1b27885b99e216bee29b955052630.zip |
Reduce Dalvik card table overhead
The VM has a "growth limit" (e.g. 48MB) that is used to cap normal
apps, and a "max heap" (e.g. 128MB) that is used for apps with unusual
high-memory requirements. The Dalvik GC uses a 128:1 "card table"
that spans the entire managed heap, out to the "max heap" limit.
The table is erased during every concurrent GC. The first time
we do that post-zygote, the copy-on-write behavior causes physical
pages to be allocated. We're currently clearing out to the heap max,
not the growth limit, which means we have a bunch of physical memory
allocated for pages that will never be used.
This changes it so we only clear out to the growth limit. If the
growth limit is removed by the app, we clear out to the heap max.
On devices with a 128MB max heap, this reduces the private/dirty
usage for most apps by 640KB.
Bug 5567332
Change-Id: Iac633017c128d63b284d48016e47d40cc46df1de
-rw-r--r-- | vm/alloc/CardTable.cpp | 23 | ||||
-rw-r--r-- | vm/alloc/CardTable.h | 2 | ||||
-rw-r--r-- | vm/alloc/Heap.cpp | 2 | ||||
-rw-r--r-- | vm/alloc/HeapInternal.h | 1 | ||||
-rw-r--r-- | vm/alloc/HeapSource.cpp | 1 |
5 files changed, 25 insertions, 4 deletions
diff --git a/vm/alloc/CardTable.cpp b/vm/alloc/CardTable.cpp index c24be0187..212c9d015 100644 --- a/vm/alloc/CardTable.cpp +++ b/vm/alloc/CardTable.cpp @@ -48,7 +48,7 @@ * Initializes the card table; must be called before any other * dvmCardTable*() functions. */ -bool dvmCardTableStartup(size_t heapMaximumSize) +bool dvmCardTableStartup(size_t heapMaximumSize, size_t growthLimit) { size_t length; void *allocBase; @@ -67,7 +67,8 @@ bool dvmCardTableStartup(size_t heapMaximumSize) return false; } gcHeap->cardTableBase = (u1*)allocBase; - gcHeap->cardTableLength = length; + gcHeap->cardTableLength = growthLimit / GC_CARD_SIZE; + gcHeap->cardTableMaxLength = length; gcHeap->cardTableOffset = 0; /* All zeros is the correct initial value; all clean. */ assert(GC_CARD_CLEAN == 0); @@ -96,8 +97,26 @@ void dvmCardTableShutdown() void dvmClearCardTable() { + /* + * The goal is to zero out some mmap-allocated pages. We can accomplish + * this with memset() or madvise(MADV_DONTNEED). The latter has some + * useful properties, notably that the pages are returned to the system, + * so cards for parts of the heap we haven't expanded into won't be + * allocated physical pages. On the other hand, if we un-map the card + * area, we'll have to fault it back in as we resume dirtying objects, + * which reduces performance. (Also, "the kernel is free to ignore the + * advice" makes this sound like something we can't necessarily rely on + * to synchronously clear memory; may need to memset *and* madvise.) + * + * TODO: use memset() to clear out to the current "soft" limit, and + * madvise() to clear out the rest. + * + * Note that cardTableLength is initially set to the growth limit, and + * on request will be expanded to the heap maximum. + */ assert(gDvm.gcHeap->cardTableBase != NULL); memset(gDvm.gcHeap->cardTableBase, GC_CARD_CLEAN, gDvm.gcHeap->cardTableLength); + //madvise(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength, MADV_DONTNEED); } /* diff --git a/vm/alloc/CardTable.h b/vm/alloc/CardTable.h index 532c25963..43b0563b1 100644 --- a/vm/alloc/CardTable.h +++ b/vm/alloc/CardTable.h @@ -32,7 +32,7 @@ * Initializes the card table; must be called before any other * dvmCardTable*() functions. */ -bool dvmCardTableStartup(size_t heapMaximumSize); +bool dvmCardTableStartup(size_t heapMaximumSize, size_t growthLimit); /* * Tears down the entire CardTable structure. diff --git a/vm/alloc/Heap.cpp b/vm/alloc/Heap.cpp index db640a6c1..9eee817e5 100644 --- a/vm/alloc/Heap.cpp +++ b/vm/alloc/Heap.cpp @@ -97,7 +97,7 @@ bool dvmHeapStartup() */ gcHeap->clearedReferences = NULL; - if (!dvmCardTableStartup(gDvm.heapMaximumSize)) { + if (!dvmCardTableStartup(gDvm.heapMaximumSize, gDvm.heapGrowthLimit)) { LOGE_HEAP("card table startup failed."); return false; } diff --git a/vm/alloc/HeapInternal.h b/vm/alloc/HeapInternal.h index a893bb259..185af1cb9 100644 --- a/vm/alloc/HeapInternal.h +++ b/vm/alloc/HeapInternal.h @@ -48,6 +48,7 @@ struct GcHeap { /* GC's card table */ u1* cardTableBase; size_t cardTableLength; + size_t cardTableMaxLength; size_t cardTableOffset; /* Is the GC running? Used to avoid recursive calls to GC. diff --git a/vm/alloc/HeapSource.cpp b/vm/alloc/HeapSource.cpp index d3779d8f9..f61724a40 100644 --- a/vm/alloc/HeapSource.cpp +++ b/vm/alloc/HeapSource.cpp @@ -1069,6 +1069,7 @@ void dvmClearGrowthLimit() HS_BOILERPLATE(); dvmLockHeap(); dvmWaitForConcurrentGcToComplete(); + gDvm.gcHeap->cardTableLength = gDvm.gcHeap->cardTableMaxLength; gHs->growthLimit = gHs->maximumSize; size_t overhead = oldHeapOverhead(gHs, false); gHs->heaps[0].maximumSize = gHs->maximumSize - overhead; |