OSDN Git Service

Revert "Upgrade to dlmalloc 2.8.5."
authorBrian Carlstrom <bdc@google.com>
Wed, 22 Aug 2012 19:11:40 +0000 (12:11 -0700)
committerBrian Carlstrom <bdc@google.com>
Wed, 22 Aug 2012 19:11:40 +0000 (12:11 -0700)
This reverts commit bba37bd191843ef29ef9c7a8839e98b73debfffa.

vm/Dvm.mk
vm/alloc/Copying.cpp
vm/alloc/DdmHeap.cpp
vm/alloc/DlMalloc.cpp [deleted file]
vm/alloc/DlMalloc.h [deleted file]
vm/alloc/Heap.cpp
vm/alloc/HeapSource.cpp
vm/alloc/HeapSource.h

index 2f53391..1b95d77 100644 (file)
--- a/vm/Dvm.mk
+++ b/vm/Dvm.mk
@@ -199,7 +199,6 @@ ifeq ($(WITH_COPYING_GC),true)
        alloc/Copying.cpp.arm
 else
   LOCAL_SRC_FILES += \
-       alloc/DlMalloc.cpp \
        alloc/HeapSource.cpp \
        alloc/MarkSweep.cpp.arm
 endif
index 77cdac3..ad5b8fc 100644 (file)
@@ -731,8 +731,9 @@ void dvmHeapSourceGrowForUtilization()
     /* do nothing */
 }
 
-void dvmHeapSourceWalk(void(*callback)(void* start, void* end,
-                                       size_t used_bytes, void* arg),
+void dvmHeapSourceWalk(void (*callback)(const void *chunkptr, size_t chunklen,
+                                        const void *userptr, size_t userlen,
+                                        void *arg),
                        void *arg)
 {
     assert(!"implemented");
index 45b0a26..ffe4cae 100644 (file)
@@ -23,7 +23,6 @@
 #include "alloc/Heap.h"
 #include "alloc/HeapInternal.h"
 #include "alloc/DdmHeap.h"
-#include "alloc/DlMalloc.h"
 #include "alloc/HeapSource.h"
 
 #define DEFAULT_HEAP_ID  1
@@ -174,7 +173,6 @@ enum HpsgKind {
     ((u1)((((kind) & 0x7) << 3) | ((solidity) & 0x7)))
 
 struct HeapChunkContext {
-    void* startOfNextMemoryChunk;
     u1 *buf;
     u1 *p;
     u1 *pieceLenField;
@@ -207,25 +205,36 @@ static void flush_hpsg_chunk(HeapChunkContext *ctx)
     ctx->pieceLenField = NULL;
 }
 
-static void append_chunk(HeapChunkContext *ctx, u1 state, void* ptr, size_t length) {
+static void heap_chunk_callback(const void *chunkptr, size_t chunklen,
+                                const void *userptr, size_t userlen, void *arg)
+{
+    HeapChunkContext *ctx = (HeapChunkContext *)arg;
+    u1 state;
+
+    UNUSED_PARAMETER(userlen);
+
+    assert((chunklen & (ALLOCATION_UNIT_SIZE-1)) == 0);
+
     /* Make sure there's enough room left in the buffer.
      * We need to use two bytes for every fractional 256
-     * allocation units used by the chunk and 17 bytes for
-     * any header.
+     * allocation units used by the chunk.
      */
     {
-        size_t needed = (((length/ALLOCATION_UNIT_SIZE + 255) / 256) * 2) + 17;
+        size_t needed = (((chunklen/ALLOCATION_UNIT_SIZE + 255) / 256) * 2);
         size_t bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf);
         if (bytesLeft < needed) {
             flush_hpsg_chunk(ctx);
         }
+
         bytesLeft = ctx->bufLen - (size_t)(ctx->p - ctx->buf);
         if (bytesLeft < needed) {
-            ALOGW("chunk is too big to transmit (length=%zd, %zd bytes)",
-                  length, needed);
+            ALOGW("chunk is too big to transmit (chunklen=%zd, %zd bytes)",
+                chunklen, needed);
             return;
         }
     }
+
+//TODO: notice when there's a gap and start a new heap, or at least a new range.
     if (ctx->needHeader) {
         /*
          * Start a new HPSx chunk.
@@ -238,7 +247,7 @@ static void append_chunk(HeapChunkContext *ctx, u1 state, void* ptr, size_t leng
         *ctx->p++ = 8;
 
         /* [u4]: virtual address of segment start */
-        set4BE(ctx->p, (uintptr_t)ptr); ctx->p += 4;
+        set4BE(ctx->p, (uintptr_t)chunkptr); ctx->p += 4;
 
         /* [u4]: offset of this piece (relative to the virtual address) */
         set4BE(ctx->p, 0); ctx->p += 4;
@@ -252,123 +261,80 @@ static void append_chunk(HeapChunkContext *ctx, u1 state, void* ptr, size_t leng
 
         ctx->needHeader = false;
     }
-    /* Write out the chunk description.
-     */
-    length /= ALLOCATION_UNIT_SIZE;   // convert to allocation units
-    ctx->totalAllocationUnits += length;
-    while (length > 256) {
-        *ctx->p++ = state | HPSG_PARTIAL;
-        *ctx->p++ = 255;     // length - 1
-        length -= 256;
-    }
-    *ctx->p++ = state;
-    *ctx->p++ = length - 1;
-}
-
-/*
- * Called by dlmalloc_inspect_all. If used_bytes != 0 then start is
- * the start of a malloc-ed piece of memory of size used_bytes. If
- * start is 0 then start is the beginning of any free space not
- * including dlmalloc's book keeping and end the start of the next
- * dlmalloc chunk. Regions purely containing book keeping don't
- * callback.
- */
-static void heap_chunk_callback(void* start, void* end, size_t used_bytes,
-                                void* arg)
-{
-    u1 state;
-    HeapChunkContext *ctx = (HeapChunkContext *)arg;
-    UNUSED_PARAMETER(end);
 
-    if (used_bytes == 0) {
-        if (start == NULL) {
-            // Reset for start of new heap.
-            ctx->startOfNextMemoryChunk = NULL;
-            flush_hpsg_chunk(ctx);
-        }
-        // Only process in use memory so that free region information
-        // also includes dlmalloc book keeping.
-        return;
-    }
-
-    /* If we're looking at the native heap, we'll just return
-     * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks
+    /* Determine the type of this chunk.
      */
-    bool native = ctx->type == CHUNK_TYPE("NHSG");
-
-    if (ctx->startOfNextMemoryChunk != NULL) {
-        // Transmit any pending free memory. Native free memory of
-        // over kMaxFreeLen could be because of the use of mmaps, so
-        // don't report. If not free memory then start a new segment.
-        bool flush = true;
-        if (start > ctx->startOfNextMemoryChunk) {
-            const size_t kMaxFreeLen = 2 * SYSTEM_PAGE_SIZE;
-            void* freeStart = ctx->startOfNextMemoryChunk;
-            void* freeEnd = start;
-            size_t freeLen = (char*)freeEnd - (char*)freeStart;
-            if (!native || freeLen < kMaxFreeLen) {
-                append_chunk(ctx, HPSG_STATE(SOLIDITY_FREE, 0),
-                             freeStart, freeLen);
-                flush = false;
-            }
-        }
-        if (flush) {
-            ctx->startOfNextMemoryChunk = NULL;
-            flush_hpsg_chunk(ctx);
-        }
-    }
-    const Object *obj = (const Object *)start;
+    if (userptr == NULL) {
+        /* It's a free chunk.
+         */
+        state = HPSG_STATE(SOLIDITY_FREE, 0);
+    } else {
+        const Object *obj = (const Object *)userptr;
+        /* If we're looking at the native heap, we'll just return
+         * (SOLIDITY_HARD, KIND_NATIVE) for all allocated chunks
+         */
+        bool native = ctx->type == CHUNK_TYPE("NHSG");
 
-    /* It's an allocated chunk.  Figure out what it is.
-     */
+        /* It's an allocated chunk.  Figure out what it is.
+         */
 //TODO: if ctx.merge, see if this chunk is different from the last chunk.
 //      If it's the same, we should combine them.
-    if (!native && dvmIsValidObject(obj)) {
-        ClassObject *clazz = obj->clazz;
-        if (clazz == NULL) {
-            /* The object was probably just created
-             * but hasn't been initialized yet.
-             */
-            state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
-        } else if (dvmIsTheClassClass(clazz)) {
-            state = HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
-        } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
-            if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
-                state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
-            } else {
-                switch (clazz->elementClass->primitiveType) {
-                case PRIM_BOOLEAN:
-                case PRIM_BYTE:
-                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
-                    break;
-                case PRIM_CHAR:
-                case PRIM_SHORT:
-                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
-                    break;
-                case PRIM_INT:
-                case PRIM_FLOAT:
+        if (!native && dvmIsValidObject(obj)) {
+            ClassObject *clazz = obj->clazz;
+            if (clazz == NULL) {
+                /* The object was probably just created
+                 * but hasn't been initialized yet.
+                 */
+                state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
+            } else if (dvmIsTheClassClass(clazz)) {
+                state = HPSG_STATE(SOLIDITY_HARD, KIND_CLASS_OBJECT);
+            } else if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
+                if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
                     state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
-                    break;
-                case PRIM_DOUBLE:
-                case PRIM_LONG:
-                    state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
-                    break;
-                default:
-                    assert(!"Unknown GC heap object type");
-                    state = HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
-                    break;
+                } else {
+                    switch (clazz->elementClass->primitiveType) {
+                    case PRIM_BOOLEAN:
+                    case PRIM_BYTE:
+                        state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_1);
+                        break;
+                    case PRIM_CHAR:
+                    case PRIM_SHORT:
+                        state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_2);
+                        break;
+                    case PRIM_INT:
+                    case PRIM_FLOAT:
+                        state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_4);
+                        break;
+                    case PRIM_DOUBLE:
+                    case PRIM_LONG:
+                        state = HPSG_STATE(SOLIDITY_HARD, KIND_ARRAY_8);
+                        break;
+                    default:
+                        assert(!"Unknown GC heap object type");
+                        state = HPSG_STATE(SOLIDITY_HARD, KIND_UNKNOWN);
+                        break;
+                    }
                 }
+            } else {
+                state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
             }
         } else {
-            state = HPSG_STATE(SOLIDITY_HARD, KIND_OBJECT);
+            obj = NULL; // it's not actually an object
+            state = HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
         }
-    } else {
-        obj = NULL; // it's not actually an object
-        state = HPSG_STATE(SOLIDITY_HARD, KIND_NATIVE);
     }
-    append_chunk(ctx, state, start, used_bytes + HEAP_SOURCE_CHUNK_OVERHEAD);
-    ctx->startOfNextMemoryChunk =
-        (char*)start + used_bytes + HEAP_SOURCE_CHUNK_OVERHEAD;
+
+    /* Write out the chunk description.
+     */
+    chunklen /= ALLOCATION_UNIT_SIZE;   // convert to allocation units
+    ctx->totalAllocationUnits += chunklen;
+    while (chunklen > 256) {
+        *ctx->p++ = state | HPSG_PARTIAL;
+        *ctx->p++ = 255;     // length - 1
+        chunklen -= 256;
+    }
+    *ctx->p++ = state;
+    *ctx->p++ = chunklen - 1;
 }
 
 enum HpsgWhen {
@@ -387,6 +353,8 @@ enum HpsgWhat {
  */
 #define HPSx_CHUNK_SIZE (16384 - 16)
 
+extern "C" void dlmalloc_walk_heap(void(*)(const void*, size_t, const void*, size_t, void*),void*);
+
 static void walkHeap(bool merge, bool native)
 {
     HeapChunkContext ctx;
@@ -412,7 +380,7 @@ static void walkHeap(bool merge, bool native)
     ctx.p = ctx.buf;
     ctx.needHeader = true;
     if (native) {
-        dlmalloc_inspect_all(heap_chunk_callback, (void*)&ctx);
+        dlmalloc_walk_heap(heap_chunk_callback, (void *)&ctx);
     } else {
         dvmHeapSourceWalk(heap_chunk_callback, (void *)&ctx);
     }
diff --git a/vm/alloc/DlMalloc.cpp b/vm/alloc/DlMalloc.cpp
deleted file mode 100644 (file)
index 8638ab5..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "DlMalloc.h"
-
-#include <stdint.h>
-#include "Common.h"
-
-/* Dalvik specific morecore implementation defined in HeapSource.cpp. */
-#define MORECORE(x) dvmHeapSourceMorecore(m, x)
-extern void* dvmHeapSourceMorecore(void* mspace, intptr_t increment);
-
-/* Custom heap error handling. */
-#define PROCEED_ON_ERROR 0
-static void heap_error(const char* msg, const char* function, void* p);
-#define CORRUPTION_ERROR_ACTION(m) \
-    heap_error("HEAP MEMORY CORRUPTION", __FUNCTION__, NULL)
-#define USAGE_ERROR_ACTION(m,p) \
-    heap_error("ARGUMENT IS INVALID HEAP ADDRESS", __FUNCTION__, p)
-
-/*
- * Ugly inclusion of C file so that Dalvik specific #defines configure
- * dlmalloc for our use for mspaces (regular dlmalloc is still declared
- * in bionic).
- */
-#pragma GCC diagnostic ignored "-Wempty-body"
-#pragma GCC diagnostic ignored "-Wstrict-aliasing"
-#include "../../../bionic/libc/upstream-dlmalloc/malloc.c"
-#pragma GCC diagnostic warning "-Wstrict-aliasing"
-#pragma GCC diagnostic warning "-Wempty-body"
-
-
-static void heap_error(const char* msg, const char* function, void* p) {
-    ALOG(LOG_FATAL, LOG_TAG, "@@@ ABORTING: DALVIK: %s IN %s addr=%p", msg,
-         function, p);
-    /* So that we can get a memory dump around p */
-    *((int **) 0xdeadbaad) = (int *) p;
-}
diff --git a/vm/alloc/DlMalloc.h b/vm/alloc/DlMalloc.h
deleted file mode 100644 (file)
index e28c7e5..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef DALVIK_VM_ALLOC_DLMALLOC_H_
-#define DALVIK_VM_ALLOC_DLMALLOC_H_
-
-/* Configure dlmalloc for mspaces. */
-#define HAVE_MMAP 0
-#define HAVE_MREMAP 0
-#define HAVE_MORECORE 1
-#define MSPACES 1
-#define NO_MALLINFO 1
-#define ONLY_MSPACES 1
-#define USE_DL_PREFIX 1
-#define MALLOC_INSPECT_ALL 1
-
-/* Include the proper definitions. */
-#include "../../../bionic/libc/upstream-dlmalloc/malloc.h"
-
-/*
- * Define dlmalloc routines from bionic that cannot be included
- * directly because of redefining symbols from the include above.
- */
-extern "C" void dlmalloc_inspect_all(void(*handler)(void*, void *, size_t, void*),
-                                     void* arg);
-extern "C" int  dlmalloc_trim(size_t);
-extern "C" void* dlmem2chunk(void* mem);
-
-#endif  // DALVIK_VM_ALLOC_DLMALLOC_H_
index d7539b0..a68a416 100644 (file)
@@ -179,6 +179,17 @@ 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.
@@ -244,6 +255,7 @@ static void *tryMalloc(size_t size)
      * 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);
index c62cde1..2815577 100644 (file)
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <cutils/mspace.h>
 #include <stdint.h>
 #include <sys/mman.h>
 #include <errno.h>
 #define SIZE_MAX UINT_MAX  // TODO: get SIZE_MAX from stdint.h
 
 #include "Dalvik.h"
-#include "alloc/DlMalloc.h"
 #include "alloc/Heap.h"
 #include "alloc/HeapInternal.h"
 #include "alloc/HeapSource.h"
 #include "alloc/HeapBitmap.h"
 #include "alloc/HeapBitmapInlines.h"
 
+// TODO: find a real header file for these.
+extern "C" int dlmalloc_trim(size_t);
+extern "C" void dlmalloc_walk_free_pages(void(*)(void*, void*, void*), void*);
+
 static void snapIdealFootprint();
 static void setIdealFootprint(size_t max);
 static size_t getMaximumSize(const HeapSource *hs);
@@ -93,12 +97,6 @@ struct Heap {
      * The highest address of this heap, exclusive.
      */
     char *limit;
-
-    /*
-     * If the heap has an mspace, the current high water mark in
-     * allocations requested via dvmHeapSourceMorecore.
-     */
-    char *brk;
 };
 
 struct HeapSource {
@@ -203,7 +201,7 @@ static size_t getAllocLimit(const HeapSource *hs)
     if (isSoftLimited(hs)) {
         return hs->softLimit;
     } else {
-        return mspace_footprint_limit(hs2heap(hs)->msp);
+        return mspace_max_allowed_footprint(hs2heap(hs)->msp);
     }
 }
 
@@ -260,7 +258,7 @@ static void countAllocation(Heap *heap, const void *ptr)
 {
     assert(heap->bytesAllocated < mspace_footprint(heap->msp));
 
-    heap->bytesAllocated += mspace_usable_size(ptr) +
+    heap->bytesAllocated += mspace_usable_size(heap->msp, ptr) +
             HEAP_SOURCE_CHUNK_OVERHEAD;
     heap->objectsAllocated++;
     HeapSource* hs = gDvm.gcHeap->heapSource;
@@ -271,7 +269,7 @@ static void countAllocation(Heap *heap, const void *ptr)
 
 static void countFree(Heap *heap, const void *ptr, size_t *numBytes)
 {
-    size_t delta = mspace_usable_size(ptr) + HEAP_SOURCE_CHUNK_OVERHEAD;
+    size_t delta = mspace_usable_size(heap->msp, ptr) + HEAP_SOURCE_CHUNK_OVERHEAD;
     assert(delta > 0);
     if (delta < heap->bytesAllocated) {
         heap->bytesAllocated -= delta;
@@ -288,64 +286,37 @@ static void countFree(Heap *heap, const void *ptr, size_t *numBytes)
 
 static HeapSource *gHs = NULL;
 
-static mspace createMspace(void* begin, size_t morecoreStart, size_t startingSize)
+static mspace createMspace(void *base, size_t startSize, size_t maximumSize)
 {
-    // Clear errno to allow strerror on error.
+    /* Create an unlocked dlmalloc mspace to use as
+     * a heap source.
+     *
+     * We start off reserving startSize / 2 bytes but
+     * letting the heap grow to startSize.  This saves
+     * memory in the case where a process uses even less
+     * than the starting size.
+     */
+    LOGV_HEAP("Creating VM heap of size %zu", startSize);
     errno = 0;
-    // Allow access to inital pages that will hold mspace.
-    mprotect(begin, morecoreStart, PROT_READ | PROT_WRITE);
-    // Create mspace using our backing storage starting at begin and with a footprint of
-    // morecoreStart. Don't use an internal dlmalloc lock. When morecoreStart bytes of memory are
-    // exhausted morecore will be called.
-    mspace msp = create_mspace_with_base(begin, morecoreStart, false /*locked*/);
+
+    mspace msp = create_contiguous_mspace_with_base(startSize/2,
+            maximumSize, /*locked=*/false, base);
     if (msp != NULL) {
-        // Do not allow morecore requests to succeed beyond the starting size of the heap.
-        mspace_set_footprint_limit(msp, startingSize);
+        /* Don't let the heap grow past the starting size without
+         * our intervention.
+         */
+        mspace_set_max_allowed_footprint(msp, startSize);
     } else {
-        ALOGE("create_mspace_with_base failed %s", strerror(errno));
+        /* There's no guarantee that errno has meaning when the call
+         * fails, but it often does.
+         */
+        LOGE_HEAP("Can't create VM heap of size (%zu,%zu): %s",
+            startSize/2, maximumSize, strerror(errno));
     }
-    return msp;
-}
 
-/*
- * Service request from DlMalloc to increase heap size.
- */
-void* dvmHeapSourceMorecore(void* mspace, intptr_t increment)
-{
-    Heap* heap = NULL;
-    for (size_t i = 0; i < gHs->numHeaps; i++) {
-        if (gHs->heaps[i].msp == mspace) {
-            heap = &gHs->heaps[i];
-            break;
-        }
-    }
-    if (heap == NULL) {
-        ALOGE("Failed to find heap for mspace %p", mspace);
-        dvmAbort();
-    }
-    char* original_brk = heap->brk;
-    if (increment != 0) {
-        char* new_brk = original_brk + increment;
-        if (increment > 0) {
-            // Should never be asked to increase the allocation beyond the capacity of the space.
-            // Enforced by mspace_set_footprint_limit.
-            assert(new_brk <= heap->limit);
-            mprotect(original_brk, increment, PROT_READ | PROT_WRITE);
-        } else {
-            // Should never be asked for negative footprint (ie before base).
-            assert(original_brk + increment > heap->base);
-            // Advise we don't need the pages and protect them.
-            size_t size = -increment;
-            madvise(new_brk, size, MADV_DONTNEED);
-            mprotect(new_brk, size, PROT_NONE);
-        }
-        // Update brk.
-        heap->brk = new_brk;
-    }
-    return original_brk;
+    return msp;
 }
 
-const size_t kInitialMorecoreStart = SYSTEM_PAGE_SIZE;
 /*
  * Add the initial heap.  Returns false if the initial heap was
  * already added to the heap source.
@@ -361,8 +332,7 @@ static bool addInitialHeap(HeapSource *hs, mspace msp, size_t maximumSize)
     hs->heaps[0].maximumSize = maximumSize;
     hs->heaps[0].concurrentStartBytes = SIZE_MAX;
     hs->heaps[0].base = hs->heapBase;
-    hs->heaps[0].limit = hs->heapBase + maximumSize;
-    hs->heaps[0].brk = hs->heapBase + kInitialMorecoreStart;
+    hs->heaps[0].limit = hs->heapBase + hs->heaps[0].maximumSize;
     hs->numHeaps = 1;
     return true;
 }
@@ -389,7 +359,8 @@ static bool addNewHeap(HeapSource *hs)
      * Heap storage comes from a common virtual memory reservation.
      * The new heap will start on the page after the old heap.
      */
-    char *base = hs->heaps[0].brk;
+    void *sbrk0 = contiguous_mspace_sbrk0(hs->heaps[0].msp);
+    char *base = (char *)ALIGN_UP_TO_PAGE_SIZE(sbrk0);
     size_t overhead = base - hs->heaps[0].base;
     assert(((size_t)hs->heaps[0].base & (SYSTEM_PAGE_SIZE - 1)) == 0);
 
@@ -399,13 +370,12 @@ static bool addNewHeap(HeapSource *hs)
                   overhead, hs->maximumSize);
         return false;
     }
-    size_t morecoreStart = SYSTEM_PAGE_SIZE;
+
     heap.maximumSize = hs->growthLimit - overhead;
     heap.concurrentStartBytes = HEAP_MIN_FREE - CONCURRENT_START;
     heap.base = base;
     heap.limit = heap.base + heap.maximumSize;
-    heap.brk = heap.base + morecoreStart;
-    heap.msp = createMspace(base, morecoreStart, HEAP_MIN_FREE);
+    heap.msp = createMspace(base, HEAP_MIN_FREE, hs->maximumSize - overhead);
     if (heap.msp == NULL) {
         return false;
     }
@@ -414,7 +384,8 @@ static bool addNewHeap(HeapSource *hs)
      */
     hs->heaps[0].maximumSize = overhead;
     hs->heaps[0].limit = base;
-    mspace_set_footprint_limit(hs->heaps[0].msp, overhead);
+    mspace msp = hs->heaps[0].msp;
+    mspace_set_max_allowed_footprint(msp, mspace_footprint(msp));
 
     /* Put the new heap in the list, at heaps[0].
      * Shift existing heaps down.
@@ -559,7 +530,7 @@ GcHeap* dvmHeapSourceStartup(size_t startSize, size_t maximumSize,
     /* Create an unlocked dlmalloc mspace to use as
      * a heap source.
      */
-    msp = createMspace(base, kInitialMorecoreStart, startSize);
+    msp = createMspace(base, startSize, maximumSize);
     if (msp == NULL) {
         goto fail;
     }
@@ -709,7 +680,7 @@ size_t dvmHeapSourceGetValue(HeapSourceValueSpec spec, size_t perHeapStats[],
             value = mspace_footprint(heap->msp);
             break;
         case HS_ALLOWED_FOOTPRINT:
-            value = mspace_footprint_limit(heap->msp);
+            value = mspace_max_allowed_footprint(heap->msp);
             break;
         case HS_BYTES_ALLOCATED:
             value = heap->bytesAllocated;
@@ -866,14 +837,14 @@ static void* heapAllocAndGrow(HeapSource *hs, Heap *heap, size_t n)
      */
     size_t max = heap->maximumSize;
 
-    mspace_set_footprint_limit(heap->msp, max);
+    mspace_set_max_allowed_footprint(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));
+    mspace_set_max_allowed_footprint(heap->msp,
+                                     mspace_footprint(heap->msp));
     return ptr;
 }
 
@@ -952,14 +923,41 @@ size_t dvmHeapSourceFreeList(size_t numPtrs, void **ptrs)
         // mspace_free, but on the other heaps we only do some
         // accounting.
         if (heap == gHs->heaps) {
-            // Count freed objects.
-            for (size_t i = 0; i < numPtrs; i++) {
+            // mspace_merge_objects takes two allocated objects, and
+            // if the second immediately follows the first, will merge
+            // them, returning a larger object occupying the same
+            // memory. This is a local operation, and doesn't require
+            // dlmalloc to manipulate any freelists. It's pretty
+            // inexpensive compared to free().
+
+            // ptrs is an array of objects all in memory order, and if
+            // client code has been allocating lots of short-lived
+            // objects, this is likely to contain runs of objects all
+            // now garbage, and thus highly amenable to this optimization.
+
+            // Unroll the 0th iteration around the loop below,
+            // countFree ptrs[0] and initializing merged.
+            assert(ptrs[0] != NULL);
+            assert(ptr2heap(gHs, ptrs[0]) == heap);
+            countFree(heap, ptrs[0], &numBytes);
+            void *merged = ptrs[0];
+            for (size_t i = 1; i < numPtrs; i++) {
+                assert(merged != NULL);
                 assert(ptrs[i] != NULL);
+                assert((intptr_t)merged < (intptr_t)ptrs[i]);
                 assert(ptr2heap(gHs, ptrs[i]) == heap);
                 countFree(heap, ptrs[i], &numBytes);
+                // Try to merge. If it works, merged now includes the
+                // memory of ptrs[i]. If it doesn't, free merged, and
+                // see if ptrs[i] starts a new run of adjacent
+                // objects to merge.
+                if (mspace_merge_objects(msp, merged, ptrs[i]) == NULL) {
+                    mspace_free(msp, merged);
+                    merged = ptrs[i];
+                }
             }
-            // Bulk free ptrs.
-            mspace_bulk_free(msp, ptrs, numPtrs);
+            assert(merged != NULL);
+            mspace_free(msp, merged);
         } else {
             // This is not an 'active heap'. Only do the accounting.
             for (size_t i = 0; i < numPtrs; i++) {
@@ -1026,7 +1024,7 @@ size_t dvmHeapSourceChunkSize(const void *ptr)
 
     Heap* heap = ptr2heap(gHs, ptr);
     if (heap != NULL) {
-        return mspace_usable_size(ptr);
+        return mspace_usable_size(heap->msp, ptr);
     }
     return 0;
 }
@@ -1124,13 +1122,13 @@ static void setSoftLimit(HeapSource *hs, size_t softLimit)
     if (softLimit < currentHeapSize) {
         /* Don't let the heap grow any more, and impose a soft limit.
          */
-        mspace_set_footprint_limit(msp, currentHeapSize);
+        mspace_set_max_allowed_footprint(msp, currentHeapSize);
         hs->softLimit = softLimit;
     } else {
         /* Let the heap grow to the requested max, and remove any
          * soft limit, if set.
          */
-        mspace_set_footprint_limit(msp, softLimit);
+        mspace_set_max_allowed_footprint(msp, softLimit);
         hs->softLimit = SIZE_MAX;
     }
 }
@@ -1286,22 +1284,17 @@ void dvmHeapSourceGrowForUtilization()
  * Return free pages to the system.
  * TODO: move this somewhere else, especially the native heap part.
  */
-static void releasePagesInRange(void* start, void* end, size_t used_bytes,
-                                void* releasedBytes)
+static void releasePagesInRange(void *start, void *end, void *nbytes)
 {
-    if (used_bytes == 0) {
-        /*
-         * We have a range of memory we can try to madvise()
-         * back. Linux requires that the madvise() start address is
-         * page-aligned.  We also align the end address.
-         */
-        start = (void *)ALIGN_UP_TO_PAGE_SIZE(start);
-        end = (void *)((size_t)end & ~(SYSTEM_PAGE_SIZE - 1));
-        if (end > start) {
-            size_t length = (char *)end - (char *)start;
-            madvise(start, length, MADV_DONTNEED);
-            *(size_t *)releasedBytes += length;
-        }
+    /* Linux requires that the madvise() start address is page-aligned.
+    * We also align the end address.
+    */
+    start = (void *)ALIGN_UP_TO_PAGE_SIZE(start);
+    end = (void *)((size_t)end & ~(SYSTEM_PAGE_SIZE - 1));
+    if (start < end) {
+        size_t length = (char *)end - (char *)start;
+        madvise(start, length, MADV_DONTNEED);
+        *(size_t *)nbytes += length;
     }
 }
 
@@ -1317,17 +1310,20 @@ static void trimHeaps()
     for (size_t i = 0; i < hs->numHeaps; i++) {
         Heap *heap = &hs->heaps[i];
 
-        /* Return the wilderness chunk to the system. */
+        /* Return the wilderness chunk to the system.
+         */
         mspace_trim(heap->msp, 0);
 
-        /* Return any whole free pages to the system. */
-        mspace_inspect_all(heap->msp, releasePagesInRange, &heapBytes);
+        /* Return any whole free pages to the system.
+         */
+        mspace_walk_free_pages(heap->msp, releasePagesInRange, &heapBytes);
     }
 
-    /* Same for the native heap. */
+    /* Same for the native heap.
+     */
     dlmalloc_trim(0);
     size_t nativeBytes = 0;
-    dlmalloc_inspect_all(releasePagesInRange, &nativeBytes);
+    dlmalloc_walk_free_pages(releasePagesInRange, &nativeBytes);
 
     LOGD_HEAP("madvised %zd (GC) + %zd (native) = %zd total bytes",
             heapBytes, nativeBytes, heapBytes + nativeBytes);
@@ -1337,8 +1333,9 @@ static void trimHeaps()
  * Walks over the heap source and passes every allocated and
  * free chunk to the callback.
  */
-void dvmHeapSourceWalk(void(*callback)(void* start, void* end,
-                                       size_t used_bytes, void* arg),
+void dvmHeapSourceWalk(void(*callback)(const void *chunkptr, size_t chunklen,
+                                       const void *userptr, size_t userlen,
+                                       void *arg),
                        void *arg)
 {
     HS_BOILERPLATE();
@@ -1348,8 +1345,7 @@ void dvmHeapSourceWalk(void(*callback)(void* start, void* end,
 //TODO: do this in address order
     HeapSource *hs = gHs;
     for (size_t i = hs->numHeaps; i > 0; --i) {
-        mspace_inspect_all(hs->heaps[i-1].msp, callback, arg);
-        callback(NULL, NULL, 0, arg);  // Indicate end of a heap.
+        mspace_walk_heap(hs->heaps[i-1].msp, callback, arg);
     }
 }
 
index 4bd11cd..66f2a6a 100644 (file)
@@ -22,6 +22,7 @@
 /* dlmalloc uses one size_t per allocated chunk.
  */
 #define HEAP_SOURCE_CHUNK_OVERHEAD         (1 * sizeof (size_t))
+#define HEAP_SOURCE_WORST_CHUNK_OVERHEAD   (32 * sizeof (size_t))
 
 /* The largest number of separate heaps we can handle.
  */
@@ -156,8 +157,9 @@ void dvmHeapSourceGrowForUtilization(void);
  * Walks over the heap source and passes every allocated and
  * free chunk to the callback.
  */
-void dvmHeapSourceWalk(void(*callback)(void* start, void* end,
-                                       size_t used_bytes, void* arg),
+void dvmHeapSourceWalk(void(*callback)(const void *chunkptr, size_t chunklen,
+                                      const void *userptr, size_t userlen,
+                                      void *arg),
                        void *arg);
 /*
  * Gets the number of heaps available in the heap source.