Implement growth limits to support multiple heap configurations.
When a growth limit is in effect, allocations will be limited to
number of bytes specified by the growth limit instead of the maximum
heap size. Growth limits are specified on the command line with the
new parameter -XX:HeapGrowthLimit. A growth limit can be removed at
runtime by calling the new clearGrowthLimit method.
This is a work around until we can adjust the maximum heap size at
runtime.
Change-Id: Ic01e32823b5ca8cf29c0948fb6cd2df10967c1fb
diff --git a/vm/Globals.h b/vm/Globals.h
index aa3be76..e48b1d2 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -74,9 +74,10 @@
char* bootClassPathStr;
char* classPathStr;
- unsigned int heapSizeStart;
- unsigned int heapSizeMax;
- unsigned int stackSize;
+ size_t heapStartingSize;
+ size_t heapMaximumSize;
+ size_t heapGrowthLimit;
+ size_t stackSize;
bool verboseGc;
bool verboseJni;
diff --git a/vm/Init.c b/vm/Init.c
index ae609c6..9fcd291 100644
--- a/vm/Init.c
+++ b/vm/Init.c
@@ -769,7 +769,7 @@
size_t val = parseMemOption(argv[i]+4, 1024);
if (val != 0) {
if (val >= kMinHeapStartSize && val <= kMaxHeapSize) {
- gDvm.heapSizeStart = val;
+ gDvm.heapStartingSize = val;
} else {
dvmFprintf(stderr,
"Invalid -Xms '%s', range is %dKB to %dKB\n",
@@ -784,7 +784,7 @@
size_t val = parseMemOption(argv[i]+4, 1024);
if (val != 0) {
if (val >= kMinHeapSize && val <= kMaxHeapSize) {
- gDvm.heapSizeMax = val;
+ gDvm.heapMaximumSize = val;
} else {
dvmFprintf(stderr,
"Invalid -Xmx '%s', range is %dKB to %dKB\n",
@@ -795,6 +795,14 @@
dvmFprintf(stderr, "Invalid -Xmx option '%s'\n", argv[i]);
return -1;
}
+ } else if (strncmp(argv[i], "-XX:HeapGrowthLimit=", 20) == 0) {
+ size_t val = parseMemOption(argv[i] + 20, 1024);
+ if (val != 0) {
+ gDvm.heapGrowthLimit = val;
+ } else {
+ dvmFprintf(stderr, "Invalid -XX:HeapGrowthLimit option '%s'\n", argv[i]);
+ return -1;
+ }
} else if (strncmp(argv[i], "-Xss", 4) == 0) {
size_t val = parseMemOption(argv[i]+4, 1);
if (val != 0) {
@@ -1057,8 +1065,9 @@
/* Defaults overridden by -Xms and -Xmx.
* TODO: base these on a system or application-specific default
*/
- gDvm.heapSizeStart = 2 * 1024 * 1024; // Spec says 16MB; too big for us.
- gDvm.heapSizeMax = 16 * 1024 * 1024; // Spec says 75% physical mem
+ gDvm.heapStartingSize = 2 * 1024 * 1024; // Spec says 16MB; too big for us.
+ gDvm.heapMaximumSize = 16 * 1024 * 1024; // Spec says 75% physical mem
+ gDvm.heapGrowthLimit = 0; // 0 means no growth limit
gDvm.stackSize = kDefaultStackSize;
gDvm.concurrentMarkSweep = true;
diff --git a/vm/alloc/Alloc.h b/vm/alloc/Alloc.h
index 988ae6c..804e40e 100644
--- a/vm/alloc/Alloc.h
+++ b/vm/alloc/Alloc.h
@@ -169,4 +169,9 @@
*/
size_t dvmCountAssignableInstancesOfClass(const ClassObject *clazz);
+/*
+ * Removes any growth limits from the heap.
+ */
+void dvmClearGrowthLimit(void);
+
#endif /*_DALVIK_ALLOC_ALLOC*/
diff --git a/vm/alloc/CardTable.c b/vm/alloc/CardTable.c
index fcfe49c..02b6c47 100644
--- a/vm/alloc/CardTable.c
+++ b/vm/alloc/CardTable.c
@@ -46,7 +46,7 @@
* Initializes the card table; must be called before any other
* dvmCardTable*() functions.
*/
-bool dvmCardTableStartup(void)
+bool dvmCardTableStartup(size_t heapMaximumSize)
{
size_t length;
void *allocBase;
@@ -57,7 +57,7 @@
assert(heapBase != NULL);
/* Set up the card table */
- length = gDvm.heapSizeMax / GC_CARD_SIZE;
+ length = heapMaximumSize / GC_CARD_SIZE;
/* Allocate an extra 256 bytes to allow fixed low-byte of base */
allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE,
"dalvik-card-table");
diff --git a/vm/alloc/CardTable.h b/vm/alloc/CardTable.h
index a6f2b38..96131d1 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(void);
+bool dvmCardTableStartup(size_t heapMaximumSize);
/*
* Tears down the entire CardTable structure.
diff --git a/vm/alloc/DdmHeap.c b/vm/alloc/DdmHeap.c
index 4cb5cae..2d661ee 100644
--- a/vm/alloc/DdmHeap.c
+++ b/vm/alloc/DdmHeap.c
@@ -107,7 +107,7 @@
*b++ = (u1)reason;
/* max allowed heap size in bytes */
- set4BE(b, gDvm.heapSizeMax); b += 4;
+ set4BE(b, dvmHeapSourceGetMaximumSize()); b += 4;
/* current heap size in bytes */
set4BE(b, dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0)); b += 4;
diff --git a/vm/alloc/Heap.c b/vm/alloc/Heap.c
index e1c0d03..39f92b5 100644
--- a/vm/alloc/Heap.c
+++ b/vm/alloc/Heap.c
@@ -51,7 +51,13 @@
{
GcHeap *gcHeap;
- gcHeap = dvmHeapSourceStartup(gDvm.heapSizeStart, gDvm.heapSizeMax);
+ if (gDvm.heapGrowthLimit == 0) {
+ gDvm.heapGrowthLimit = gDvm.heapMaximumSize;
+ }
+
+ gcHeap = dvmHeapSourceStartup(gDvm.heapStartingSize,
+ gDvm.heapMaximumSize,
+ gDvm.heapGrowthLimit);
if (gcHeap == NULL) {
return false;
}
@@ -73,7 +79,7 @@
gcHeap->pendingFinalizationRefs = NULL;
gcHeap->referenceOperations = NULL;
- if (!dvmCardTableStartup()) {
+ if (!dvmCardTableStartup(gDvm.heapMaximumSize)) {
LOGE_HEAP("card table startup failed.");
return false;
}
@@ -236,7 +242,7 @@
* going to succeed. We have to collect SoftReferences before
* throwing an OOME, though.
*/
- if (size >= gDvm.heapSizeMax) {
+ if (size >= gDvm.heapGrowthLimit) {
LOGW_HEAP("dvmMalloc(%zu/0x%08zx): "
"someone's allocating a huge buffer\n", size, size);
ptr = NULL;
diff --git a/vm/alloc/HeapDebug.c b/vm/alloc/HeapDebug.c
index 986f16b..df7d648 100644
--- a/vm/alloc/HeapDebug.c
+++ b/vm/alloc/HeapDebug.c
@@ -29,6 +29,8 @@
return (int)dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
case kVirtualHeapAllocated:
return (int)dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
+ case kVirtualHeapMaximumSize:
+ return dvmHeapSourceGetMaximumSize();
default:
return -1;
}
diff --git a/vm/alloc/HeapDebug.h b/vm/alloc/HeapDebug.h
index 19f4b45..f36b7ab 100644
--- a/vm/alloc/HeapDebug.h
+++ b/vm/alloc/HeapDebug.h
@@ -21,6 +21,7 @@
kNativeHeapSize = 1,
kVirtualHeapAllocated = 2,
kNativeHeapAllocated = 3,
+ kVirtualHeapMaximumSize = 4
} HeapDebugInfoType;
/* Return the specified value.
diff --git a/vm/alloc/HeapSource.c b/vm/alloc/HeapSource.c
index 06ebb68..bf51e70 100644
--- a/vm/alloc/HeapSource.c
+++ b/vm/alloc/HeapSource.c
@@ -31,6 +31,7 @@
static void snapIdealFootprint(void);
static void setIdealFootprint(size_t max);
+static size_t getMaximumSize(const HeapSource *hs);
#define ALIGN_UP_TO_PAGE_SIZE(p) \
(((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
@@ -114,6 +115,16 @@
*/
size_t maximumSize;
+ /*
+ * The largest size we permit the heap to grow. This value allows
+ * the user to limit the heap growth below the maximum size. This
+ * is a work around until we can dynamically set the maximum size.
+ * This value can range between the starting size and the maximum
+ * size but should never be set below the current footprint of the
+ * heap.
+ */
+ size_t growthLimit;
+
/* The desired max size of the heap source as a whole.
*/
size_t idealSize;
@@ -321,7 +332,7 @@
}
static bool
-addNewHeap(HeapSource *hs, mspace msp, size_t mspAbsoluteMaxSize)
+addNewHeap(HeapSource *hs, mspace msp, size_t maximumSize)
{
Heap heap;
@@ -336,7 +347,7 @@
if (msp != NULL) {
heap.msp = msp;
- heap.maximumSize = mspAbsoluteMaxSize;
+ heap.maximumSize = maximumSize;
heap.concurrentStartBytes = SIZE_MAX;
heap.base = hs->heapBase;
heap.limit = hs->heapBase + heap.maximumSize;
@@ -354,7 +365,7 @@
}
hs->heaps[0].maximumSize = overhead;
hs->heaps[0].limit = base;
- heap.maximumSize = hs->maximumSize - overhead;
+ heap.maximumSize = hs->growthLimit - overhead;
heap.msp = createMspace(base, HEAP_MIN_FREE, heap.maximumSize);
heap.concurrentStartBytes = HEAP_MIN_FREE - CONCURRENT_START;
heap.base = base;
@@ -460,7 +471,7 @@
* allocated from the heap source.
*/
GcHeap *
-dvmHeapSourceStartup(size_t startSize, size_t maximumSize)
+dvmHeapSourceStartup(size_t startSize, size_t maximumSize, size_t growthLimit)
{
GcHeap *gcHeap;
HeapSource *hs;
@@ -470,9 +481,9 @@
assert(gHs == NULL);
- if (startSize > maximumSize) {
- LOGE("Bad heap parameters (start=%d, max=%d)\n",
- startSize, maximumSize);
+ if (!(startSize <= growthLimit && growthLimit <= maximumSize)) {
+ LOGE("Bad heap size parameters (start=%zd, max=%zd, limit=%zd)",
+ startSize, maximumSize, growthLimit);
return NULL;
}
@@ -513,6 +524,7 @@
hs->targetUtilization = DEFAULT_HEAP_UTILIZATION;
hs->startSize = startSize;
hs->maximumSize = maximumSize;
+ hs->growthLimit = growthLimit;
hs->idealSize = startSize;
hs->softLimit = SIZE_MAX; // no soft limit at first
hs->numHeaps = 0;
@@ -520,7 +532,7 @@
hs->hasGcThread = false;
hs->heapBase = base;
hs->heapLength = length;
- if (!addNewHeap(hs, msp, maximumSize)) {
+ if (!addNewHeap(hs, msp, growthLimit)) {
LOGE_HEAP("Can't add initial heap\n");
goto fail;
}
@@ -1075,6 +1087,38 @@
return oldHeapOverhead(gHs, true);
}
+static size_t getMaximumSize(const HeapSource *hs)
+{
+ return hs->growthLimit;
+}
+
+/*
+ * Returns the current maximum size of the heap source respecting any
+ * growth limits.
+ */
+size_t dvmHeapSourceGetMaximumSize()
+{
+ HS_BOILERPLATE();
+ return getMaximumSize(gHs);
+}
+
+/*
+ * Removes any growth limits. Allows the user to allocate up to the
+ * maximum heap size.
+ */
+void dvmClearGrowthLimit()
+{
+ size_t overhead;
+
+ HS_BOILERPLATE();
+ dvmLockHeap();
+ dvmWaitForConcurrentGcToComplete();
+ gHs->growthLimit = gHs->maximumSize;
+ overhead = oldHeapOverhead(gHs, false);
+ gHs->heaps[0].maximumSize = gHs->maximumSize - overhead;
+ dvmUnlockHeap();
+}
+
/*
* Return the real bytes used by old heaps plus the soft usage of the
* current heap. When a soft limit is in effect, this is effectively
@@ -1153,14 +1197,16 @@
size_t oldAllowedFootprint =
mspace_max_allowed_footprint(msp);
#endif
+ size_t maximumSize;
HS_BOILERPLATE();
- if (max > hs->maximumSize) {
+ maximumSize = getMaximumSize(hs);
+ if (max > maximumSize) {
LOGI_HEAP("Clamp target GC heap from %zd.%03zdMB to %u.%03uMB\n",
FRACTIONAL_MB(max),
- FRACTIONAL_MB(hs->maximumSize));
- max = hs->maximumSize;
+ FRACTIONAL_MB(maximumSize));
+ max = maximumSize;
}
/* Convert max into a size that applies to the active heap.
diff --git a/vm/alloc/HeapSource.h b/vm/alloc/HeapSource.h
index f48120b..70bc128 100644
--- a/vm/alloc/HeapSource.h
+++ b/vm/alloc/HeapSource.h
@@ -32,7 +32,9 @@
* Initializes the heap source; must be called before any other
* dvmHeapSource*() functions.
*/
-GcHeap *dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize);
+GcHeap *dvmHeapSourceStartup(size_t startingSize,
+ size_t maximumSize,
+ size_t growthLimit);
/*
* If the HeapSource was created while in zygote mode, this
@@ -197,4 +199,10 @@
*/
void *dvmHeapSourceGetImmuneLimit(GcMode mode);
+/*
+ * Returns the maximum size of the heap. This value will be either
+ * the value of -Xmx or a user supplied growth limit.
+ */
+size_t dvmHeapSourceGetMaximumSize(void);
+
#endif // _DALVIK_HEAP_SOURCE
diff --git a/vm/native/dalvik_system_VMRuntime.c b/vm/native/dalvik_system_VMRuntime.c
index 542961c..fec24be 100644
--- a/vm/native/dalvik_system_VMRuntime.c
+++ b/vm/native/dalvik_system_VMRuntime.c
@@ -165,6 +165,13 @@
RETURN_LONG(result);
}
+static void Dalvik_dalvik_system_VMRuntime_clearGrowthLimit(const u4* args,
+ JValue* pResult)
+{
+ dvmClearGrowthLimit();
+ RETURN_VOID();
+}
+
const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = {
{ "getTargetHeapUtilization", "()F",
Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization },
@@ -182,5 +189,7 @@
Dalvik_dalvik_system_VMRuntime_newNonMovableArray },
{ "addressOf", "(Ljava/lang/Object;)J",
Dalvik_dalvik_system_VMRuntime_addressOf },
+ { "clearGrowthLimit", "()V",
+ Dalvik_dalvik_system_VMRuntime_clearGrowthLimit },
{ NULL, NULL, NULL },
};
diff --git a/vm/native/java_lang_Runtime.c b/vm/native/java_lang_Runtime.c
index d28ff84..90df259 100644
--- a/vm/native/java_lang_Runtime.c
+++ b/vm/native/java_lang_Runtime.c
@@ -138,38 +138,37 @@
RETURN_INT((int)result);
}
/*
- * public void maxMemory()
+ * public long maxMemory()
*
* Returns GC heap max memory in bytes.
*/
static void Dalvik_java_lang_Runtime_maxMemory(const u4* args, JValue* pResult)
{
- unsigned int result = gDvm.heapSizeMax;
- RETURN_LONG(result);
+ RETURN_LONG(dvmGetHeapDebugInfo(kVirtualHeapMaximumSize));
}
/*
- * public void totalMemory()
+ * public long totalMemory()
*
* Returns GC heap total memory in bytes.
*/
static void Dalvik_java_lang_Runtime_totalMemory(const u4* args,
JValue* pResult)
{
- int result = dvmGetHeapDebugInfo(kVirtualHeapSize);
- RETURN_LONG(result);
+ RETURN_LONG(dvmGetHeapDebugInfo(kVirtualHeapSize));
}
/*
- * public void freeMemory()
+ * public long freeMemory()
*
* Returns GC heap free memory in bytes.
*/
static void Dalvik_java_lang_Runtime_freeMemory(const u4* args,
JValue* pResult)
{
- int result = dvmGetHeapDebugInfo(kVirtualHeapSize)
- - dvmGetHeapDebugInfo(kVirtualHeapAllocated);
+ size_t size = dvmGetHeapDebugInfo(kVirtualHeapSize);
+ size_t allocated = dvmGetHeapDebugInfo(kVirtualHeapAllocated);
+ long long result = size - allocated;
if (result < 0) {
result = 0;
}