HeapSource.c revision 06f254ec0102b43fa4faed2483befd945bc12996
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <cutils/mspace.h>
18#include <limits.h>     // for INT_MAX
19#include <sys/mman.h>
20#include <errno.h>
21
22#include "Dalvik.h"
23#include "alloc/Heap.h"
24#include "alloc/HeapInternal.h"
25#include "alloc/HeapSource.h"
26#include "alloc/HeapBitmap.h"
27
28// TODO: find a real header file for these.
29extern int dlmalloc_trim(size_t);
30extern void dlmalloc_walk_free_pages(void(*)(void*, void*, void*), void*);
31
32static void snapIdealFootprint(void);
33static void setIdealFootprint(size_t max);
34
35#define ALIGN_UP_TO_PAGE_SIZE(p) \
36    (((size_t)(p) + (SYSTEM_PAGE_SIZE - 1)) & ~(SYSTEM_PAGE_SIZE - 1))
37#define ALIGN_DOWN_TO_PAGE_SIZE(p) \
38    ((size_t)(p) & ~(SYSTEM_PAGE_SIZE - 1))
39
40#define HEAP_UTILIZATION_MAX        1024
41#define DEFAULT_HEAP_UTILIZATION    512     // Range 1..HEAP_UTILIZATION_MAX
42#define HEAP_IDEAL_FREE             (2 * 1024 * 1024)
43#define HEAP_MIN_FREE               (HEAP_IDEAL_FREE / 4)
44
45#define HS_BOILERPLATE() \
46    do { \
47        assert(gDvm.gcHeap != NULL); \
48        assert(gDvm.gcHeap->heapSource != NULL); \
49        assert(gHs == gDvm.gcHeap->heapSource); \
50    } while (0)
51
52#define DEBUG_HEAP_SOURCE 0
53#if DEBUG_HEAP_SOURCE
54#define HSTRACE(...)  LOG(LOG_INFO, LOG_TAG "-hs", __VA_ARGS__)
55#else
56#define HSTRACE(...)  /**/
57#endif
58
59/*
60=======================================================
61=======================================================
62=======================================================
63
64How will this be used?
65allocating/freeing: Heap.c just wants to say "alloc(n)" and get a ptr
66    - if allocating in large doesn't work, try allocating from small
67Heap.c will use HeapSource.h; HeapSource.c will do the right thing
68    between small and large
69    - some operations should be abstracted; put in a structure
70
71How do we manage the size trade-offs?
72- keep mspace max footprint clamped to actual footprint
73- if small-alloc returns null, adjust large vs. small ratio
74    - give small all available slack and retry
75    - success or fail, snap back to actual footprint and give rest to large
76
77managed as "small actual" + "large actual" + "delta to allowed total footprint"
78- when allocating from one source or the other, give the delta to the
79    active source, but snap back afterwards
80- that may not work so great for a gc heap, because small will always consume.
81    - but we need to use the memory, and the current max is the amount we
82      need to fill before a GC.
83
84Find a way to permanently steal pages from the middle of the heap
85    - segment tricks?
86
87Allocate String and char[] in a separate heap?
88
89Maybe avoid growing small heap, even if there's slack?  Look at
90live ratio of small heap after a gc; scale it based on that.
91
92=======================================================
93=======================================================
94=======================================================
95*/
96
97typedef struct {
98    /* The mspace to allocate from.
99     */
100    mspace msp;
101
102    /* The bitmap that keeps track of where objects are in the heap.
103     */
104    HeapBitmap objectBitmap;
105
106    /* The largest size that this heap is allowed to grow to.
107     */
108    size_t absoluteMaxSize;
109
110    /* Number of bytes allocated from this mspace for objects,
111     * including any overhead.  This value is NOT exact, and
112     * should only be used as an input for certain heuristics.
113     */
114    size_t bytesAllocated;
115
116    /* Number of objects currently allocated from this mspace.
117     */
118    size_t objectsAllocated;
119} Heap;
120
121struct HeapSource {
122    /* Target ideal heap utilization ratio; range 1..HEAP_UTILIZATION_MAX
123     */
124    size_t targetUtilization;
125
126    /* Requested minimum heap size, or zero if there is no minimum.
127     */
128    size_t minimumSize;
129
130    /* The starting heap size.
131     */
132    size_t startSize;
133
134    /* The largest that the heap source as a whole is allowed to grow.
135     */
136    size_t absoluteMaxSize;
137
138    /* The desired max size of the heap source as a whole.
139     */
140    size_t idealSize;
141
142    /* The maximum number of bytes allowed to be allocated from the
143     * active heap before a GC is forced.  This is used to "shrink" the
144     * heap in lieu of actual compaction.
145     */
146    size_t softLimit;
147
148    /* The heaps; heaps[0] is always the active heap,
149     * which new objects should be allocated from.
150     */
151    Heap heaps[HEAP_SOURCE_MAX_HEAP_COUNT];
152
153    /* The current number of heaps.
154     */
155    size_t numHeaps;
156
157    /* External allocation count.
158     */
159    size_t externalBytesAllocated;
160
161    /* The maximum number of external bytes that may be allocated.
162     */
163    size_t externalLimit;
164
165    /* True if zygote mode was active when the HeapSource was created.
166     */
167    bool sawZygote;
168};
169
170#define hs2heap(hs_) (&((hs_)->heaps[0]))
171
172/*
173 * Returns true iff a soft limit is in effect for the active heap.
174 */
175static inline bool
176softLimited(const HeapSource *hs)
177{
178    /* softLimit will be either INT_MAX or the limit for the
179     * active mspace.  idealSize can be greater than softLimit
180     * if there is more than one heap.  If there is only one
181     * heap, a non-INT_MAX softLimit should always be the same
182     * as idealSize.
183     */
184    return hs->softLimit <= hs->idealSize;
185}
186
187/*
188 * Returns the current footprint of all heaps.  If includeActive
189 * is false, don't count the heap at index 0.
190 */
191static inline size_t
192oldHeapOverhead(const HeapSource *hs, bool includeActive)
193{
194    size_t footprint = 0;
195    size_t i;
196
197    if (includeActive) {
198        i = 0;
199    } else {
200        i = 1;
201    }
202    for (/* i = i */; i < hs->numHeaps; i++) {
203//TODO: include size of bitmaps?  If so, don't use bitsLen, listen to .max
204        footprint += mspace_footprint(hs->heaps[i].msp);
205    }
206    return footprint;
207}
208
209/*
210 * Returns the heap that <ptr> could have come from, or NULL
211 * if it could not have come from any heap.
212 */
213static inline Heap *
214ptr2heap(const HeapSource *hs, const void *ptr)
215{
216    const size_t numHeaps = hs->numHeaps;
217    size_t i;
218
219//TODO: unroll this to HEAP_SOURCE_MAX_HEAP_COUNT
220    if (ptr != NULL) {
221        for (i = 0; i < numHeaps; i++) {
222            const Heap *const heap = &hs->heaps[i];
223
224            if (dvmHeapBitmapMayContainObject(&heap->objectBitmap, ptr)) {
225                return (Heap *)heap;
226            }
227        }
228    }
229    return NULL;
230}
231
232/*
233 * Functions to update heapSource->bytesAllocated when an object
234 * is allocated or freed.  mspace_usable_size() will give
235 * us a much more accurate picture of heap utilization than
236 * the requested byte sizes would.
237 *
238 * These aren't exact, and should not be treated as such.
239 */
240static inline void
241countAllocation(Heap *heap, const void *ptr, bool isObj)
242{
243    assert(heap->bytesAllocated < mspace_footprint(heap->msp));
244
245    heap->bytesAllocated += mspace_usable_size(heap->msp, ptr) +
246            HEAP_SOURCE_CHUNK_OVERHEAD;
247    if (isObj) {
248        heap->objectsAllocated++;
249        dvmHeapBitmapSetObjectBit(&heap->objectBitmap, ptr);
250    }
251
252    assert(heap->bytesAllocated < mspace_footprint(heap->msp));
253}
254
255static inline void
256countFree(Heap *heap, const void *ptr, bool isObj)
257{
258    size_t delta;
259
260    delta = mspace_usable_size(heap->msp, ptr) + HEAP_SOURCE_CHUNK_OVERHEAD;
261    assert(delta > 0);
262    if (delta < heap->bytesAllocated) {
263        heap->bytesAllocated -= delta;
264    } else {
265        heap->bytesAllocated = 0;
266    }
267    if (isObj) {
268        dvmHeapBitmapClearObjectBit(&heap->objectBitmap, ptr);
269        if (heap->objectsAllocated > 0) {
270            heap->objectsAllocated--;
271        }
272    }
273}
274
275static HeapSource *gHs = NULL;
276
277static mspace
278createMspace(size_t startSize, size_t absoluteMaxSize, size_t id)
279{
280    mspace msp;
281    char name[PATH_MAX];
282
283    /* If two ashmem regions have the same name, only one gets
284     * the name when looking at the maps.
285     */
286    snprintf(name, sizeof(name)-1, "dalvik-heap%s/%zd",
287        gDvm.zygote ? "/zygote" : "", id);
288    name[sizeof(name)-1] = '\0';
289
290    /* Create an unlocked dlmalloc mspace to use as
291     * a small-object heap source.
292     *
293     * We start off reserving heapSizeStart/2 bytes but
294     * letting the heap grow to heapSizeStart.  This saves
295     * memory in the case where a process uses even less
296     * than the starting size.
297     */
298    LOGV_HEAP("Creating VM heap of size %u\n", startSize);
299    errno = 0;
300    msp = create_contiguous_mspace_with_name(startSize/2,
301            absoluteMaxSize, /*locked=*/false, name);
302    if (msp != NULL) {
303        /* Don't let the heap grow past the starting size without
304         * our intervention.
305         */
306        mspace_set_max_allowed_footprint(msp, startSize);
307    } else {
308        /* There's no guarantee that errno has meaning when the call
309         * fails, but it often does.
310         */
311        LOGE_HEAP("Can't create VM heap of size (%u,%u) (errno=%d)\n",
312            startSize/2, absoluteMaxSize, errno);
313    }
314
315    return msp;
316}
317
318static bool
319addNewHeap(HeapSource *hs, mspace msp, size_t mspAbsoluteMaxSize)
320{
321    Heap heap;
322
323    if (hs->numHeaps >= HEAP_SOURCE_MAX_HEAP_COUNT) {
324        LOGE("Attempt to create too many heaps (%zd >= %zd)\n",
325                hs->numHeaps, HEAP_SOURCE_MAX_HEAP_COUNT);
326        dvmAbort();
327        return false;
328    }
329
330    memset(&heap, 0, sizeof(heap));
331
332    if (msp != NULL) {
333        heap.msp = msp;
334        heap.absoluteMaxSize = mspAbsoluteMaxSize;
335    } else {
336        size_t overhead;
337
338        overhead = oldHeapOverhead(hs, true);
339        if (overhead + HEAP_MIN_FREE >= hs->absoluteMaxSize) {
340            LOGE_HEAP("No room to create any more heaps "
341                    "(%zd overhead, %zd max)\n",
342                    overhead, hs->absoluteMaxSize);
343            return false;
344        }
345        heap.absoluteMaxSize = hs->absoluteMaxSize - overhead;
346        heap.msp = createMspace(HEAP_MIN_FREE, heap.absoluteMaxSize,
347                hs->numHeaps);
348        if (heap.msp == NULL) {
349            return false;
350        }
351    }
352    if (!dvmHeapBitmapInit(&heap.objectBitmap,
353                           (void *)ALIGN_DOWN_TO_PAGE_SIZE(heap.msp),
354                           heap.absoluteMaxSize,
355                           "objects"))
356    {
357        LOGE_HEAP("Can't create objectBitmap\n");
358        goto fail;
359    }
360
361    /* Don't let the soon-to-be-old heap grow any further.
362     */
363    if (hs->numHeaps > 0) {
364        mspace msp = hs->heaps[0].msp;
365        mspace_set_max_allowed_footprint(msp, mspace_footprint(msp));
366    }
367
368    /* Put the new heap in the list, at heaps[0].
369     * Shift existing heaps down.
370     */
371    memmove(&hs->heaps[1], &hs->heaps[0], hs->numHeaps * sizeof(hs->heaps[0]));
372    hs->heaps[0] = heap;
373    hs->numHeaps++;
374
375    return true;
376
377fail:
378    if (msp == NULL) {
379        destroy_contiguous_mspace(heap.msp);
380    }
381    return false;
382}
383
384/*
385 * Initializes the heap source; must be called before any other
386 * dvmHeapSource*() functions.  Returns a GcHeap structure
387 * allocated from the heap source.
388 */
389GcHeap *
390dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize)
391{
392    GcHeap *gcHeap;
393    HeapSource *hs;
394    Heap *heap;
395    mspace msp;
396
397    assert(gHs == NULL);
398
399    if (startSize > absoluteMaxSize) {
400        LOGE("Bad heap parameters (start=%d, max=%d)\n",
401           startSize, absoluteMaxSize);
402        return NULL;
403    }
404
405    /* Create an unlocked dlmalloc mspace to use as
406     * the small object heap source.
407     */
408    msp = createMspace(startSize, absoluteMaxSize, 0);
409    if (msp == NULL) {
410        return false;
411    }
412
413    /* Allocate a descriptor from the heap we just created.
414     */
415    gcHeap = mspace_malloc(msp, sizeof(*gcHeap));
416    if (gcHeap == NULL) {
417        LOGE_HEAP("Can't allocate heap descriptor\n");
418        goto fail;
419    }
420    memset(gcHeap, 0, sizeof(*gcHeap));
421
422    hs = mspace_malloc(msp, sizeof(*hs));
423    if (hs == NULL) {
424        LOGE_HEAP("Can't allocate heap source\n");
425        goto fail;
426    }
427    memset(hs, 0, sizeof(*hs));
428
429    hs->targetUtilization = DEFAULT_HEAP_UTILIZATION;
430    hs->minimumSize = 0;
431    hs->startSize = startSize;
432    hs->absoluteMaxSize = absoluteMaxSize;
433    hs->idealSize = startSize;
434    hs->softLimit = INT_MAX;    // no soft limit at first
435    hs->numHeaps = 0;
436    hs->sawZygote = gDvm.zygote;
437    if (!addNewHeap(hs, msp, absoluteMaxSize)) {
438        LOGE_HEAP("Can't add initial heap\n");
439        goto fail;
440    }
441
442    gcHeap->heapSource = hs;
443
444    countAllocation(hs2heap(hs), gcHeap, false);
445    countAllocation(hs2heap(hs), hs, false);
446
447    gHs = hs;
448    return gcHeap;
449
450fail:
451    destroy_contiguous_mspace(msp);
452    return NULL;
453}
454
455/*
456 * If the HeapSource was created while in zygote mode, this
457 * will create a new heap for post-zygote allocations.
458 * Having a separate heap should maximize the number of pages
459 * that a given app_process shares with the zygote process.
460 */
461bool
462dvmHeapSourceStartupAfterZygote()
463{
464    HeapSource *hs = gHs; // use a local to avoid the implicit "volatile"
465
466    HS_BOILERPLATE();
467
468    assert(!gDvm.zygote);
469
470    if (hs->sawZygote) {
471        /* Create a new heap for post-zygote allocations.
472         */
473        return addNewHeap(hs, NULL, 0);
474    }
475    return true;
476}
477
478/*
479 * This is called while in zygote mode, right before we fork() for the
480 * first time.  We create a heap for all future zygote process allocations,
481 * in an attempt to avoid touching pages in the zygote heap.  (This would
482 * probably be unnecessary if we had a compacting GC -- the source of our
483 * troubles is small allocations filling in the gaps from larger ones.)
484 */
485bool
486dvmHeapSourceStartupBeforeFork()
487{
488    HeapSource *hs = gHs; // use a local to avoid the implicit "volatile"
489
490    HS_BOILERPLATE();
491
492    assert(gDvm.zygote);
493
494    if (!gDvm.newZygoteHeapAllocated) {
495        /* Create a new heap for post-fork zygote allocations.  We only
496         * try once, even if it fails.
497         */
498        LOGV("Splitting out new zygote heap\n");
499        gDvm.newZygoteHeapAllocated = true;
500        return addNewHeap(hs, NULL, 0);
501    }
502    return true;
503}
504
505/*
506 * Tears down the heap source and frees any resources associated with it.
507 */
508void
509dvmHeapSourceShutdown(GcHeap *gcHeap)
510{
511    if (gcHeap != NULL && gcHeap->heapSource != NULL) {
512        HeapSource *hs;
513        size_t numHeaps;
514        size_t i;
515
516        hs = gcHeap->heapSource;
517        gHs = NULL;
518
519        /* Cache numHeaps because hs will be invalid after the last
520         * heap is freed.
521         */
522        numHeaps = hs->numHeaps;
523
524        for (i = 0; i < numHeaps; i++) {
525            Heap *heap = &hs->heaps[i];
526
527            dvmHeapBitmapDelete(&heap->objectBitmap);
528            destroy_contiguous_mspace(heap->msp);
529        }
530        /* The last heap is the original one, which contains the
531         * HeapSource object itself.
532         */
533    }
534}
535
536/*
537 * Returns the requested value. If the per-heap stats are requested, fill
538 * them as well.
539 *
540 * Caller must hold the heap lock.
541 */
542size_t
543dvmHeapSourceGetValue(enum HeapSourceValueSpec spec, size_t perHeapStats[],
544                      size_t arrayLen)
545{
546    HeapSource *hs = gHs;
547    size_t value = 0;
548    size_t total = 0;
549    size_t i;
550
551    HS_BOILERPLATE();
552
553    switch (spec) {
554    case HS_EXTERNAL_BYTES_ALLOCATED:
555        return hs->externalBytesAllocated;
556    case HS_EXTERNAL_LIMIT:
557        return hs->externalLimit;
558    default:
559        // look at all heaps.
560        ;
561    }
562
563    assert(arrayLen >= hs->numHeaps || perHeapStats == NULL);
564    for (i = 0; i < hs->numHeaps; i++) {
565        Heap *const heap = &hs->heaps[i];
566
567        switch (spec) {
568        case HS_FOOTPRINT:
569            value = mspace_footprint(heap->msp);
570            break;
571        case HS_ALLOWED_FOOTPRINT:
572            value = mspace_max_allowed_footprint(heap->msp);
573            break;
574        case HS_BYTES_ALLOCATED:
575            value = heap->bytesAllocated;
576            break;
577        case HS_OBJECTS_ALLOCATED:
578            value = heap->objectsAllocated;
579            break;
580        default:
581            // quiet gcc
582            break;
583        }
584        if (perHeapStats) {
585            perHeapStats[i] = value;
586        }
587        total += value;
588    }
589    return total;
590}
591
592/*
593 * Writes shallow copies of the currently-used bitmaps into outBitmaps,
594 * returning the number of bitmaps written.  Returns <0 if the array
595 * was not long enough.
596 */
597ssize_t
598dvmHeapSourceGetObjectBitmaps(HeapBitmap outBitmaps[], size_t maxBitmaps)
599{
600    HeapSource *hs = gHs;
601
602    HS_BOILERPLATE();
603
604    if (maxBitmaps >= hs->numHeaps) {
605        size_t i;
606
607        for (i = 0; i < hs->numHeaps; i++) {
608            outBitmaps[i] = hs->heaps[i].objectBitmap;
609        }
610        return i;
611    }
612    return -1;
613}
614
615/*
616 * Replaces the object location HeapBitmaps with the elements of
617 * <objectBitmaps>.  The elements of <objectBitmaps> are overwritten
618 * with shallow copies of the old bitmaps.
619 *
620 * Returns false if the number of bitmaps doesn't match the number
621 * of heaps.
622 */
623bool
624dvmHeapSourceReplaceObjectBitmaps(HeapBitmap objectBitmaps[], size_t nBitmaps)
625{
626    HeapSource *hs = gHs;
627    size_t i;
628
629    HS_BOILERPLATE();
630
631    if (nBitmaps != hs->numHeaps) {
632        return false;
633    }
634
635    for (i = 0; i < hs->numHeaps; i++) {
636        Heap *heap = &hs->heaps[i];
637        HeapBitmap swap;
638
639        swap = heap->objectBitmap;
640        heap->objectBitmap = objectBitmaps[i];
641        objectBitmaps[i] = swap;
642    }
643    return true;
644}
645
646/*
647 * Allocates <n> bytes of zeroed data.
648 */
649void *
650dvmHeapSourceAlloc(size_t n)
651{
652    HeapSource *hs = gHs;
653    Heap *heap;
654    void *ptr;
655
656    HS_BOILERPLATE();
657    heap = hs2heap(hs);
658
659    if (heap->bytesAllocated + n <= hs->softLimit) {
660// TODO: allocate large blocks (>64k?) as separate mmap regions so that
661//       they don't increase the high-water mark when they're freed.
662// TODO: zero out large objects using madvise
663        ptr = mspace_calloc(heap->msp, 1, n);
664        if (ptr != NULL) {
665            countAllocation(heap, ptr, true);
666        }
667    } else {
668        /* This allocation would push us over the soft limit;
669         * act as if the heap is full.
670         */
671        LOGV_HEAP("softLimit of %zd.%03zdMB hit for %zd-byte allocation\n",
672                FRACTIONAL_MB(hs->softLimit), n);
673        ptr = NULL;
674    }
675    return ptr;
676}
677
678/* Remove any hard limits, try to allocate, and shrink back down.
679 * Last resort when trying to allocate an object.
680 */
681static void *
682heapAllocAndGrow(HeapSource *hs, Heap *heap, size_t n)
683{
684    void *ptr;
685    size_t max;
686
687    /* Grow as much as possible, but don't let the real footprint
688     * plus external allocations go over the absolute max.
689     */
690    max = heap->absoluteMaxSize;
691    if (max > hs->externalBytesAllocated) {
692        max -= hs->externalBytesAllocated;
693
694        mspace_set_max_allowed_footprint(heap->msp, max);
695        ptr = dvmHeapSourceAlloc(n);
696
697        /* Shrink back down as small as possible.  Our caller may
698         * readjust max_allowed to a more appropriate value.
699         */
700        mspace_set_max_allowed_footprint(heap->msp,
701                mspace_footprint(heap->msp));
702    } else {
703        ptr = NULL;
704    }
705
706    return ptr;
707}
708
709/*
710 * Allocates <n> bytes of zeroed data, growing as much as possible
711 * if necessary.
712 */
713void *
714dvmHeapSourceAllocAndGrow(size_t n)
715{
716    HeapSource *hs = gHs;
717    Heap *heap;
718    void *ptr;
719    size_t oldIdealSize;
720
721    HS_BOILERPLATE();
722    heap = hs2heap(hs);
723
724    ptr = dvmHeapSourceAlloc(n);
725    if (ptr != NULL) {
726        return ptr;
727    }
728
729    oldIdealSize = hs->idealSize;
730    if (softLimited(hs)) {
731        /* We're soft-limited.  Try removing the soft limit to
732         * see if we can allocate without actually growing.
733         */
734        hs->softLimit = INT_MAX;
735        ptr = dvmHeapSourceAlloc(n);
736        if (ptr != NULL) {
737            /* Removing the soft limit worked;  fix things up to
738             * reflect the new effective ideal size.
739             */
740            snapIdealFootprint();
741            return ptr;
742        }
743        // softLimit intentionally left at INT_MAX.
744    }
745
746    /* We're not soft-limited.  Grow the heap to satisfy the request.
747     * If this call fails, no footprints will have changed.
748     */
749    ptr = heapAllocAndGrow(hs, heap, n);
750    if (ptr != NULL) {
751        /* The allocation succeeded.  Fix up the ideal size to
752         * reflect any footprint modifications that had to happen.
753         */
754        snapIdealFootprint();
755    } else {
756        /* We just couldn't do it.  Restore the original ideal size,
757         * fixing up softLimit if necessary.
758         */
759        setIdealFootprint(oldIdealSize);
760    }
761    return ptr;
762}
763
764/*
765 * Frees the memory pointed to by <ptr>, which may be NULL.
766 */
767void
768dvmHeapSourceFree(void *ptr)
769{
770    Heap *heap;
771
772    HS_BOILERPLATE();
773
774    heap = ptr2heap(gHs, ptr);
775    if (heap != NULL) {
776        countFree(heap, ptr, true);
777        /* Only free objects that are in the active heap.
778         * Touching old heaps would pull pages into this process.
779         */
780        if (heap == gHs->heaps) {
781            mspace_free(heap->msp, ptr);
782        }
783    }
784}
785
786/*
787 * Frees the first numPtrs objects in the ptrs list. The list must
788 * contain addresses all in the same mspace, and must be in increasing
789 * order. This implies that there are no duplicates, and no entries
790 * are NULL.
791 */
792void
793dvmHeapSourceFreeList(size_t numPtrs, void **ptrs)
794{
795    Heap *heap;
796
797    HS_BOILERPLATE();
798
799    if (numPtrs == 0) {
800        return;
801    }
802
803    assert(ptrs != NULL);
804    assert(*ptrs != NULL);
805    heap = ptr2heap(gHs, *ptrs);
806    if (heap != NULL) {
807        mspace *msp = heap->msp;
808        // Calling mspace_free on shared heaps disrupts sharing too
809        // much. For heap[0] -- the 'active heap' -- we call
810        // mspace_free, but on the other heaps we only do some
811        // accounting.
812        if (heap == gHs->heaps) {
813            // mspace_merge_objects takes two allocated objects, and
814            // if the second immediately follows the first, will merge
815            // them, returning a larger object occupying the same
816            // memory. This is a local operation, and doesn't require
817            // dlmalloc to manipulate any freelists. It's pretty
818            // inexpensive compared to free().
819
820            // ptrs is an array of objects all in memory order, and if
821            // client code has been allocating lots of short-lived
822            // objects, this is likely to contain runs of objects all
823            // now garbage, and thus highly amenable to this optimization.
824
825            // Unroll the 0th iteration around the loop below,
826            // countFree ptrs[0] and initializing merged.
827            assert(ptrs[0] != NULL);
828            assert(ptr2heap(gHs, ptrs[0]) == heap);
829            countFree(heap, ptrs[0], true);
830            void *merged = ptrs[0];
831
832            size_t i;
833            for (i = 1; i < numPtrs; i++) {
834                assert(merged != NULL);
835                assert(ptrs[i] != NULL);
836                assert((intptr_t)merged < (intptr_t)ptrs[i]);
837                assert(ptr2heap(gHs, ptrs[i]) == heap);
838                countFree(heap, ptrs[i], true);
839                // Try to merge. If it works, merged now includes the
840                // memory of ptrs[i]. If it doesn't, free merged, and
841                // see if ptrs[i] starts a new run of adjacent
842                // objects to merge.
843                if (mspace_merge_objects(msp, merged, ptrs[i]) == NULL) {
844                    mspace_free(msp, merged);
845                    merged = ptrs[i];
846                }
847            }
848            assert(merged != NULL);
849            mspace_free(msp, merged);
850        } else {
851            // This is not an 'active heap'. Only do the accounting.
852            size_t i;
853            for (i = 0; i < numPtrs; i++) {
854                assert(ptrs[i] != NULL);
855                assert(ptr2heap(gHs, ptrs[i]) == heap);
856                countFree(heap, ptrs[i], true);
857            }
858        }
859    }
860}
861
862/*
863 * Returns true iff <ptr> was allocated from the heap source.
864 */
865bool
866dvmHeapSourceContains(const void *ptr)
867{
868    Heap *heap;
869
870    HS_BOILERPLATE();
871
872    heap = ptr2heap(gHs, ptr);
873    if (heap != NULL) {
874        return dvmHeapBitmapIsObjectBitSet(&heap->objectBitmap, ptr) != 0;
875    }
876    return false;
877}
878
879/*
880 * Returns the value of the requested flag.
881 */
882bool
883dvmHeapSourceGetPtrFlag(const void *ptr, enum HeapSourcePtrFlag flag)
884{
885    if (ptr == NULL) {
886        return false;
887    }
888
889    if (flag == HS_CONTAINS) {
890        return dvmHeapSourceContains(ptr);
891    } else if (flag == HS_ALLOCATED_IN_ZYGOTE) {
892        HeapSource *hs = gHs;
893
894        HS_BOILERPLATE();
895
896        if (hs->sawZygote) {
897            Heap *heap;
898
899            heap = ptr2heap(hs, ptr);
900            if (heap != NULL) {
901                /* If the object is not in the active heap, we assume that
902                 * it was allocated as part of zygote.
903                 */
904                return heap != hs->heaps;
905            }
906        }
907        /* The pointer is outside of any known heap, or we are not
908         * running in zygote mode.
909         */
910        return false;
911    }
912
913    return false;
914}
915
916/*
917 * Returns the number of usable bytes in an allocated chunk; the size
918 * may be larger than the size passed to dvmHeapSourceAlloc().
919 */
920size_t
921dvmHeapSourceChunkSize(const void *ptr)
922{
923    Heap *heap;
924
925    HS_BOILERPLATE();
926
927    heap = ptr2heap(gHs, ptr);
928    if (heap != NULL) {
929        return mspace_usable_size(heap->msp, ptr);
930    }
931    return 0;
932}
933
934/*
935 * Returns the number of bytes that the heap source has allocated
936 * from the system using sbrk/mmap, etc.
937 *
938 * Caller must hold the heap lock.
939 */
940size_t
941dvmHeapSourceFootprint()
942{
943    HS_BOILERPLATE();
944
945//TODO: include size of bitmaps?
946    return oldHeapOverhead(gHs, true);
947}
948
949/*
950 * Return the real bytes used by old heaps and external memory
951 * plus the soft usage of the current heap.  When a soft limit
952 * is in effect, this is effectively what it's compared against
953 * (though, in practice, it only looks at the current heap).
954 */
955static size_t
956getSoftFootprint(bool includeActive)
957{
958    HeapSource *hs = gHs;
959    size_t ret;
960
961    HS_BOILERPLATE();
962
963    ret = oldHeapOverhead(hs, false) + hs->externalBytesAllocated;
964    if (includeActive) {
965        ret += hs->heaps[0].bytesAllocated;
966    }
967
968    return ret;
969}
970
971/*
972 * Gets the maximum number of bytes that the heap source is allowed
973 * to allocate from the system.
974 */
975size_t
976dvmHeapSourceGetIdealFootprint()
977{
978    HeapSource *hs = gHs;
979
980    HS_BOILERPLATE();
981
982    return hs->idealSize;
983}
984
985/*
986 * Sets the soft limit, handling any necessary changes to the allowed
987 * footprint of the active heap.
988 */
989static void
990setSoftLimit(HeapSource *hs, size_t softLimit)
991{
992    /* Compare against the actual footprint, rather than the
993     * max_allowed, because the heap may not have grown all the
994     * way to the allowed size yet.
995     */
996    mspace msp = hs->heaps[0].msp;
997    size_t currentHeapSize = mspace_footprint(msp);
998    if (softLimit < currentHeapSize) {
999        /* Don't let the heap grow any more, and impose a soft limit.
1000         */
1001        mspace_set_max_allowed_footprint(msp, currentHeapSize);
1002        hs->softLimit = softLimit;
1003    } else {
1004        /* Let the heap grow to the requested max, and remove any
1005         * soft limit, if set.
1006         */
1007        mspace_set_max_allowed_footprint(msp, softLimit);
1008        hs->softLimit = INT_MAX;
1009    }
1010}
1011
1012/*
1013 * Sets the maximum number of bytes that the heap source is allowed
1014 * to allocate from the system.  Clamps to the appropriate maximum
1015 * value.
1016 */
1017static void
1018setIdealFootprint(size_t max)
1019{
1020    HeapSource *hs = gHs;
1021#if DEBUG_HEAP_SOURCE
1022    HeapSource oldHs = *hs;
1023    mspace msp = hs->heaps[0].msp;
1024    size_t oldAllowedFootprint =
1025            mspace_max_allowed_footprint(msp);
1026#endif
1027
1028    HS_BOILERPLATE();
1029
1030    if (max > hs->absoluteMaxSize) {
1031        LOGI_HEAP("Clamp target GC heap from %zd.%03zdMB to %u.%03uMB\n",
1032                FRACTIONAL_MB(max),
1033                FRACTIONAL_MB(hs->absoluteMaxSize));
1034        max = hs->absoluteMaxSize;
1035    } else if (max < hs->minimumSize) {
1036        max = hs->minimumSize;
1037    }
1038
1039    /* Convert max into a size that applies to the active heap.
1040     * Old heaps and external allocations will count against the ideal size.
1041     */
1042    size_t overhead = getSoftFootprint(false);
1043    size_t activeMax;
1044    if (overhead < max) {
1045        activeMax = max - overhead;
1046    } else {
1047        activeMax = 0;
1048    }
1049
1050    setSoftLimit(hs, activeMax);
1051    hs->idealSize = max;
1052
1053    HSTRACE("IDEAL %zd->%zd (%d), soft %zd->%zd (%d), allowed %zd->%zd (%d), "
1054            "ext %zd\n",
1055            oldHs.idealSize, hs->idealSize, hs->idealSize - oldHs.idealSize,
1056            oldHs.softLimit, hs->softLimit, hs->softLimit - oldHs.softLimit,
1057            oldAllowedFootprint, mspace_max_allowed_footprint(msp),
1058            mspace_max_allowed_footprint(msp) - oldAllowedFootprint,
1059            hs->externalBytesAllocated);
1060
1061}
1062
1063/*
1064 * Make the ideal footprint equal to the current footprint.
1065 */
1066static void
1067snapIdealFootprint()
1068{
1069    HeapSource *hs = gHs;
1070
1071    HS_BOILERPLATE();
1072
1073    setIdealFootprint(getSoftFootprint(true));
1074}
1075
1076/*
1077 * Gets the current ideal heap utilization, represented as a number
1078 * between zero and one.
1079 */
1080float dvmGetTargetHeapUtilization()
1081{
1082    HeapSource *hs = gHs;
1083
1084    HS_BOILERPLATE();
1085
1086    return (float)hs->targetUtilization / (float)HEAP_UTILIZATION_MAX;
1087}
1088
1089/*
1090 * Sets the new ideal heap utilization, represented as a number
1091 * between zero and one.
1092 */
1093void dvmSetTargetHeapUtilization(float newTarget)
1094{
1095    HeapSource *hs = gHs;
1096    size_t newUtilization;
1097
1098    HS_BOILERPLATE();
1099
1100    /* Clamp it to a reasonable range.
1101     */
1102    // TODO: This may need some tuning.
1103    if (newTarget < 0.2) {
1104        newTarget = 0.2;
1105    } else if (newTarget > 0.8) {
1106        newTarget = 0.8;
1107    }
1108
1109    hs->targetUtilization =
1110            (size_t)(newTarget * (float)HEAP_UTILIZATION_MAX);
1111    LOGV("Set heap target utilization to %zd/%d (%f)\n",
1112            hs->targetUtilization, HEAP_UTILIZATION_MAX, newTarget);
1113}
1114
1115/*
1116 * If set is true, sets the new minimum heap size to size; always
1117 * returns the current (or previous) size.  If size is negative,
1118 * removes the current minimum constraint (if present).
1119 */
1120size_t
1121dvmMinimumHeapSize(size_t size, bool set)
1122{
1123    HeapSource *hs = gHs;
1124    size_t oldMinimumSize;
1125
1126    /* gHs caches an entry in gDvm.gcHeap;  we need to hold the
1127     * heap lock if we're going to look at it.  We also need the
1128     * lock for the call to setIdealFootprint().
1129     */
1130    dvmLockHeap();
1131
1132    HS_BOILERPLATE();
1133
1134    oldMinimumSize = hs->minimumSize;
1135
1136    if (set) {
1137        /* Don't worry about external allocations right now.
1138         * setIdealFootprint() will take them into account when
1139         * minimumSize is used, and it's better to hold onto the
1140         * intended minimumSize than to clamp it arbitrarily based
1141         * on the current allocations.
1142         */
1143        if (size > hs->absoluteMaxSize) {
1144            size = hs->absoluteMaxSize;
1145        }
1146        hs->minimumSize = size;
1147        if (size > hs->idealSize) {
1148            /* Force a snap to the minimum value, which we just set
1149             * and which setIdealFootprint() will take into consideration.
1150             */
1151            setIdealFootprint(hs->idealSize);
1152        }
1153        /* Otherwise we'll just keep it in mind the next time
1154         * setIdealFootprint() is called.
1155         */
1156    }
1157
1158    dvmUnlockHeap();
1159
1160    return oldMinimumSize;
1161}
1162
1163/*
1164 * Given the size of a live set, returns the ideal heap size given
1165 * the current target utilization and MIN/MAX values.
1166 *
1167 * targetUtilization is in the range 1..HEAP_UTILIZATION_MAX.
1168 */
1169static size_t
1170getUtilizationTarget(const HeapSource *hs,
1171        size_t liveSize, size_t targetUtilization)
1172{
1173    size_t targetSize;
1174
1175    /* Use the current target utilization ratio to determine the
1176     * ideal heap size based on the size of the live set.
1177     */
1178    targetSize = (liveSize / targetUtilization) * HEAP_UTILIZATION_MAX;
1179
1180    /* Cap the amount of free space, though, so we don't end up
1181     * with, e.g., 8MB of free space when the live set size hits 8MB.
1182     */
1183    if (targetSize > liveSize + HEAP_IDEAL_FREE) {
1184        targetSize = liveSize + HEAP_IDEAL_FREE;
1185    } else if (targetSize < liveSize + HEAP_MIN_FREE) {
1186        targetSize = liveSize + HEAP_MIN_FREE;
1187    }
1188    return targetSize;
1189}
1190
1191/*
1192 * Given the current contents of the active heap, increase the allowed
1193 * heap footprint to match the target utilization ratio.  This
1194 * should only be called immediately after a full mark/sweep.
1195 */
1196void dvmHeapSourceGrowForUtilization()
1197{
1198    HeapSource *hs = gHs;
1199    Heap *heap;
1200    size_t targetHeapSize;
1201    size_t currentHeapUsed;
1202    size_t oldIdealSize;
1203    size_t newHeapMax;
1204    size_t overhead;
1205
1206    HS_BOILERPLATE();
1207    heap = hs2heap(hs);
1208
1209    /* Use the current target utilization ratio to determine the
1210     * ideal heap size based on the size of the live set.
1211     * Note that only the active heap plays any part in this.
1212     *
1213     * Avoid letting the old heaps influence the target free size,
1214     * because they may be full of objects that aren't actually
1215     * in the working set.  Just look at the allocated size of
1216     * the current heap.
1217     */
1218    currentHeapUsed = heap->bytesAllocated;
1219#define LET_EXTERNAL_INFLUENCE_UTILIZATION 1
1220#if LET_EXTERNAL_INFLUENCE_UTILIZATION
1221    /* This is a hack to deal with the side-effects of moving
1222     * bitmap data out of the Dalvik heap.  Since the amount
1223     * of free space after a GC scales with the size of the
1224     * live set, many apps expected the large free space that
1225     * appeared along with megabytes' worth of bitmaps.  When
1226     * the bitmaps were removed, the free size shrank significantly,
1227     * and apps started GCing constantly.  This makes it so the
1228     * post-GC free space is the same size it would have been
1229     * if the bitmaps were still in the Dalvik heap.
1230     */
1231    currentHeapUsed += hs->externalBytesAllocated;
1232#endif
1233    targetHeapSize =
1234            getUtilizationTarget(hs, currentHeapUsed, hs->targetUtilization);
1235#if LET_EXTERNAL_INFLUENCE_UTILIZATION
1236    currentHeapUsed -= hs->externalBytesAllocated;
1237    targetHeapSize -= hs->externalBytesAllocated;
1238#endif
1239
1240    /* The ideal size includes the old heaps; add overhead so that
1241     * it can be immediately subtracted again in setIdealFootprint().
1242     * If the target heap size would exceed the max, setIdealFootprint()
1243     * will clamp it to a legal value.
1244     */
1245    overhead = getSoftFootprint(false);
1246    oldIdealSize = hs->idealSize;
1247    setIdealFootprint(targetHeapSize + overhead);
1248
1249    newHeapMax = mspace_max_allowed_footprint(heap->msp);
1250    if (softLimited(hs)) {
1251        LOGD_HEAP("GC old usage %zd.%zd%%; now "
1252                "%zd.%03zdMB used / %zd.%03zdMB soft max "
1253                "(%zd.%03zdMB over, "
1254                "%zd.%03zdMB ext, "
1255                "%zd.%03zdMB real max)\n",
1256                FRACTIONAL_PCT(currentHeapUsed, oldIdealSize),
1257                FRACTIONAL_MB(currentHeapUsed),
1258                FRACTIONAL_MB(hs->softLimit),
1259                FRACTIONAL_MB(overhead),
1260                FRACTIONAL_MB(hs->externalBytesAllocated),
1261                FRACTIONAL_MB(newHeapMax));
1262    } else {
1263        LOGD_HEAP("GC old usage %zd.%zd%%; now "
1264                "%zd.%03zdMB used / %zd.%03zdMB real max "
1265                "(%zd.%03zdMB over, "
1266                "%zd.%03zdMB ext)\n",
1267                FRACTIONAL_PCT(currentHeapUsed, oldIdealSize),
1268                FRACTIONAL_MB(currentHeapUsed),
1269                FRACTIONAL_MB(newHeapMax),
1270                FRACTIONAL_MB(overhead),
1271                FRACTIONAL_MB(hs->externalBytesAllocated));
1272    }
1273}
1274
1275/*
1276 * Return free pages to the system.
1277 * TODO: move this somewhere else, especially the native heap part.
1278 */
1279
1280static void releasePagesInRange(void *start, void *end, void *nbytes)
1281{
1282    /* Linux requires that the madvise() start address is page-aligned.
1283    * We also align the end address.
1284    */
1285    start = (void *)ALIGN_UP_TO_PAGE_SIZE(start);
1286    end = (void *)((size_t)end & ~(SYSTEM_PAGE_SIZE - 1));
1287    if (start < end) {
1288        size_t length = (char *)end - (char *)start;
1289        madvise(start, length, MADV_DONTNEED);
1290        *(size_t *)nbytes += length;
1291    }
1292}
1293
1294/*
1295 * Return unused memory to the system if possible.
1296 */
1297void
1298dvmHeapSourceTrim(size_t bytesTrimmed[], size_t arrayLen)
1299{
1300    HeapSource *hs = gHs;
1301    size_t nativeBytes, heapBytes;
1302    size_t i;
1303
1304    HS_BOILERPLATE();
1305
1306    assert(arrayLen >= hs->numHeaps);
1307
1308    heapBytes = 0;
1309    for (i = 0; i < hs->numHeaps; i++) {
1310        Heap *heap = &hs->heaps[i];
1311
1312        /* Return the wilderness chunk to the system.
1313         */
1314        mspace_trim(heap->msp, 0);
1315
1316        /* Return any whole free pages to the system.
1317         */
1318        bytesTrimmed[i] = 0;
1319        mspace_walk_free_pages(heap->msp, releasePagesInRange,
1320                               &bytesTrimmed[i]);
1321        heapBytes += bytesTrimmed[i];
1322    }
1323
1324    /* Same for the native heap.
1325     */
1326    dlmalloc_trim(0);
1327    nativeBytes = 0;
1328    dlmalloc_walk_free_pages(releasePagesInRange, &nativeBytes);
1329
1330    LOGD_HEAP("madvised %zd (GC) + %zd (native) = %zd total bytes\n",
1331            heapBytes, nativeBytes, heapBytes + nativeBytes);
1332}
1333
1334/*
1335 * Walks over the heap source and passes every allocated and
1336 * free chunk to the callback.
1337 */
1338void
1339dvmHeapSourceWalk(void(*callback)(const void *chunkptr, size_t chunklen,
1340                                      const void *userptr, size_t userlen,
1341                                      void *arg),
1342                  void *arg)
1343{
1344    HeapSource *hs = gHs;
1345    size_t i;
1346
1347    HS_BOILERPLATE();
1348
1349    /* Walk the heaps from oldest to newest.
1350     */
1351//TODO: do this in address order
1352    for (i = hs->numHeaps; i > 0; --i) {
1353        mspace_walk_heap(hs->heaps[i-1].msp, callback, arg);
1354    }
1355}
1356
1357/*
1358 * Gets the number of heaps available in the heap source.
1359 *
1360 * Caller must hold the heap lock, because gHs caches a field
1361 * in gDvm.gcHeap.
1362 */
1363size_t
1364dvmHeapSourceGetNumHeaps()
1365{
1366    HeapSource *hs = gHs;
1367
1368    HS_BOILERPLATE();
1369
1370    return hs->numHeaps;
1371}
1372
1373
1374/*
1375 * External allocation tracking
1376 *
1377 * In some situations, memory outside of the heap is tied to the
1378 * lifetime of objects in the heap.  Since that memory is kept alive
1379 * by heap objects, it should provide memory pressure that can influence
1380 * GCs.
1381 */
1382
1383
1384static bool
1385externalAllocPossible(const HeapSource *hs, size_t n)
1386{
1387    const Heap *heap;
1388    size_t currentHeapSize;
1389
1390    /* Make sure that this allocation is even possible.
1391     * Don't let the external size plus the actual heap size
1392     * go over the absolute max.  This essentially treats
1393     * external allocations as part of the active heap.
1394     *
1395     * Note that this will fail "mysteriously" if there's
1396     * a small softLimit but a large heap footprint.
1397     */
1398    heap = hs2heap(hs);
1399    currentHeapSize = mspace_max_allowed_footprint(heap->msp);
1400    if (currentHeapSize + hs->externalBytesAllocated + n <=
1401            heap->absoluteMaxSize)
1402    {
1403        return true;
1404    }
1405    HSTRACE("externalAllocPossible(): "
1406            "footprint %zu + extAlloc %zu + n %zu >= max %zu (space for %zu)\n",
1407            currentHeapSize, hs->externalBytesAllocated, n,
1408            heap->absoluteMaxSize,
1409            heap->absoluteMaxSize -
1410                    (currentHeapSize + hs->externalBytesAllocated));
1411    return false;
1412}
1413
1414#define EXTERNAL_TARGET_UTILIZATION 820  // 80%
1415
1416/*
1417 * Tries to update the internal count of externally-allocated memory.
1418 * If there's enough room for that memory, returns true.  If not, returns
1419 * false and does not update the count.
1420 *
1421 * The caller must ensure externalAllocPossible(hs, n) == true.
1422 */
1423static bool
1424externalAlloc(HeapSource *hs, size_t n, bool grow)
1425{
1426    Heap *heap;
1427    size_t currentHeapSize;
1428    size_t newTotal;
1429    size_t max;
1430    bool grew;
1431
1432    assert(hs->externalLimit >= hs->externalBytesAllocated);
1433
1434    HSTRACE("externalAlloc(%zd%s)\n", n, grow ? ", grow" : "");
1435    assert(externalAllocPossible(hs, n));  // The caller must ensure this.
1436
1437    /* External allocations have their own "free space" that they
1438     * can allocate from without causing a GC.
1439     */
1440    if (hs->externalBytesAllocated + n <= hs->externalLimit) {
1441        hs->externalBytesAllocated += n;
1442#if defined(WITH_PROFILER) && PROFILE_EXTERNAL_ALLOCATIONS
1443        if (gDvm.allocProf.enabled) {
1444            Thread* self = dvmThreadSelf();
1445            gDvm.allocProf.externalAllocCount++;
1446            gDvm.allocProf.externalAllocSize += n;
1447            if (self != NULL) {
1448                self->allocProf.externalAllocCount++;
1449                self->allocProf.externalAllocSize += n;
1450            }
1451        }
1452#endif
1453        return true;
1454    }
1455    if (!grow) {
1456        return false;
1457    }
1458
1459    /* GROW */
1460    hs->externalBytesAllocated += n;
1461    hs->externalLimit = getUtilizationTarget(hs,
1462            hs->externalBytesAllocated, EXTERNAL_TARGET_UTILIZATION);
1463    HSTRACE("EXTERNAL grow limit to %zd\n", hs->externalLimit);
1464    return true;
1465}
1466
1467static void
1468gcForExternalAlloc(bool collectSoftReferences)
1469{
1470#ifdef WITH_PROFILER  // even if !PROFILE_EXTERNAL_ALLOCATIONS
1471    if (gDvm.allocProf.enabled) {
1472        Thread* self = dvmThreadSelf();
1473        gDvm.allocProf.gcCount++;
1474        if (self != NULL) {
1475            self->allocProf.gcCount++;
1476        }
1477    }
1478#endif
1479    dvmCollectGarbageInternal(collectSoftReferences);
1480}
1481
1482/*
1483 * Updates the internal count of externally-allocated memory.  If there's
1484 * enough room for that memory, returns true.  If not, returns false and
1485 * does not update the count.
1486 *
1487 * May cause a GC as a side-effect.
1488 */
1489bool
1490dvmTrackExternalAllocation(size_t n)
1491{
1492    HeapSource *hs = gHs;
1493    size_t overhead;
1494    bool ret = false;
1495
1496    /* gHs caches an entry in gDvm.gcHeap;  we need to hold the
1497     * heap lock if we're going to look at it.
1498     */
1499    dvmLockHeap();
1500
1501    HS_BOILERPLATE();
1502    assert(hs->externalLimit >= hs->externalBytesAllocated);
1503
1504    if (!externalAllocPossible(hs, n)) {
1505        LOGE_HEAP("%zd-byte external allocation "
1506                "too large for this process.\n", n);
1507        goto out;
1508    }
1509
1510    /* Try "allocating" using the existing "free space".
1511     */
1512    HSTRACE("EXTERNAL alloc %zu (%zu < %zu)\n",
1513            n, hs->externalBytesAllocated, hs->externalLimit);
1514    if (externalAlloc(hs, n, false)) {
1515        ret = true;
1516        goto out;
1517    }
1518
1519    /* The "allocation" failed.  Free up some space by doing
1520     * a full garbage collection.  This may grow the heap source
1521     * if the live set is sufficiently large.
1522     */
1523    HSTRACE("EXTERNAL alloc %zd: GC 1\n", n);
1524    gcForExternalAlloc(false);  // don't collect SoftReferences
1525    if (externalAlloc(hs, n, false)) {
1526        ret = true;
1527        goto out;
1528    }
1529
1530    /* Even that didn't work;  this is an exceptional state.
1531     * Try harder, growing the heap source if necessary.
1532     */
1533    HSTRACE("EXTERNAL alloc %zd: frag\n", n);
1534    ret = externalAlloc(hs, n, true);
1535    dvmHeapSizeChanged();
1536    if (ret) {
1537        goto out;
1538    }
1539
1540    /* We couldn't even grow enough to satisfy the request.
1541     * Try one last GC, collecting SoftReferences this time.
1542     */
1543    HSTRACE("EXTERNAL alloc %zd: GC 2\n", n);
1544    gcForExternalAlloc(true);  // collect SoftReferences
1545    ret = externalAlloc(hs, n, true);
1546    dvmHeapSizeChanged();
1547    if (!ret) {
1548        LOGE_HEAP("Out of external memory on a %zu-byte allocation.\n", n);
1549    }
1550
1551#if defined(WITH_PROFILER) && PROFILE_EXTERNAL_ALLOCATIONS
1552    if (gDvm.allocProf.enabled) {
1553        Thread* self = dvmThreadSelf();
1554        gDvm.allocProf.failedExternalAllocCount++;
1555        gDvm.allocProf.failedExternalAllocSize += n;
1556        if (self != NULL) {
1557            self->allocProf.failedExternalAllocCount++;
1558            self->allocProf.failedExternalAllocSize += n;
1559        }
1560    }
1561#endif
1562
1563out:
1564    dvmUnlockHeap();
1565
1566    return ret;
1567}
1568
1569/*
1570 * Reduces the internal count of externally-allocated memory.
1571 */
1572void
1573dvmTrackExternalFree(size_t n)
1574{
1575    HeapSource *hs = gHs;
1576    size_t newIdealSize;
1577    size_t newExternalLimit;
1578    size_t oldExternalBytesAllocated;
1579
1580    HSTRACE("EXTERNAL free %zu (%zu < %zu)\n",
1581            n, hs->externalBytesAllocated, hs->externalLimit);
1582
1583    /* gHs caches an entry in gDvm.gcHeap;  we need to hold the
1584     * heap lock if we're going to look at it.
1585     */
1586    dvmLockHeap();
1587
1588    HS_BOILERPLATE();
1589    assert(hs->externalLimit >= hs->externalBytesAllocated);
1590
1591    oldExternalBytesAllocated = hs->externalBytesAllocated;
1592    if (n <= hs->externalBytesAllocated) {
1593        hs->externalBytesAllocated -= n;
1594    } else {
1595        n = hs->externalBytesAllocated;
1596        hs->externalBytesAllocated = 0;
1597    }
1598
1599#if defined(WITH_PROFILER) && PROFILE_EXTERNAL_ALLOCATIONS
1600    if (gDvm.allocProf.enabled) {
1601        Thread* self = dvmThreadSelf();
1602        gDvm.allocProf.externalFreeCount++;
1603        gDvm.allocProf.externalFreeSize += n;
1604        if (self != NULL) {
1605            self->allocProf.externalFreeCount++;
1606            self->allocProf.externalFreeSize += n;
1607        }
1608    }
1609#endif
1610
1611    /* Shrink as quickly as we can.
1612     */
1613    newExternalLimit = getUtilizationTarget(hs,
1614            hs->externalBytesAllocated, EXTERNAL_TARGET_UTILIZATION);
1615    if (newExternalLimit < oldExternalBytesAllocated) {
1616        /* Make sure that the remaining free space is at least
1617         * big enough to allocate something of the size that was
1618         * just freed.  This makes it more likely that
1619         *     externalFree(N); externalAlloc(N);
1620         * will work without causing a GC.
1621         */
1622        HSTRACE("EXTERNAL free preserved %zu extra free bytes\n",
1623                oldExternalBytesAllocated - newExternalLimit);
1624        newExternalLimit = oldExternalBytesAllocated;
1625    }
1626    if (newExternalLimit < hs->externalLimit) {
1627        hs->externalLimit = newExternalLimit;
1628    }
1629
1630    dvmUnlockHeap();
1631}
1632
1633/*
1634 * Returns the number of externally-allocated bytes being tracked by
1635 * dvmTrackExternalAllocation/Free().
1636 */
1637size_t
1638dvmGetExternalBytesAllocated()
1639{
1640    const HeapSource *hs = gHs;
1641    size_t ret;
1642
1643    /* gHs caches an entry in gDvm.gcHeap;  we need to hold the
1644     * heap lock if we're going to look at it.  We also need the
1645     * lock for the call to setIdealFootprint().
1646     */
1647    dvmLockHeap();
1648    HS_BOILERPLATE();
1649    ret = hs->externalBytesAllocated;
1650    dvmUnlockHeap();
1651
1652    return ret;
1653}
1654