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