• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  * Garbage-collecting memory allocator.
18  */
19 #include "Dalvik.h"
20 #include "alloc/HeapBitmap.h"
21 #include "alloc/Verify.h"
22 #include "alloc/HeapTable.h"
23 #include "alloc/Heap.h"
24 #include "alloc/HeapInternal.h"
25 #include "alloc/DdmHeap.h"
26 #include "alloc/HeapSource.h"
27 #include "alloc/MarkSweep.h"
28 #include "alloc/Visit.h"
29 
30 #include "utils/threads.h"      // need Android thread priorities
31 #define kInvalidPriority        10000
32 
33 #include <cutils/sched_policy.h>
34 
35 #include <sys/time.h>
36 #include <sys/resource.h>
37 #include <limits.h>
38 #include <errno.h>
39 
40 static const char* GcReasonStr[] = {
41     [GC_FOR_MALLOC] = "GC_FOR_MALLOC",
42     [GC_CONCURRENT] = "GC_CONCURRENT",
43     [GC_EXPLICIT] = "GC_EXPLICIT",
44     [GC_EXTERNAL_ALLOC] = "GC_EXTERNAL_ALLOC",
45     [GC_HPROF_DUMP_HEAP] = "GC_HPROF_DUMP_HEAP"
46 };
47 
48 /*
49  * Initialize the GC heap.
50  *
51  * Returns true if successful, false otherwise.
52  */
dvmHeapStartup()53 bool dvmHeapStartup()
54 {
55     GcHeap *gcHeap;
56 
57 #if defined(WITH_ALLOC_LIMITS)
58     gDvm.checkAllocLimits = false;
59     gDvm.allocationLimit = -1;
60 #endif
61 
62     gcHeap = dvmHeapSourceStartup(gDvm.heapSizeStart, gDvm.heapSizeMax);
63     if (gcHeap == NULL) {
64         return false;
65     }
66     gcHeap->heapWorkerCurrentObject = NULL;
67     gcHeap->heapWorkerCurrentMethod = NULL;
68     gcHeap->heapWorkerInterpStartTime = 0LL;
69     gcHeap->ddmHpifWhen = 0;
70     gcHeap->ddmHpsgWhen = 0;
71     gcHeap->ddmHpsgWhat = 0;
72     gcHeap->ddmNhsgWhen = 0;
73     gcHeap->ddmNhsgWhat = 0;
74 #if WITH_HPROF
75     gcHeap->hprofDumpOnGc = false;
76     gcHeap->hprofContext = NULL;
77 #endif
78     gDvm.gcHeap = gcHeap;
79 
80     /* Set up the lists and lock we'll use for finalizable
81      * and reference objects.
82      */
83     dvmInitMutex(&gDvm.heapWorkerListLock);
84     gcHeap->finalizableRefs = NULL;
85     gcHeap->pendingFinalizationRefs = NULL;
86     gcHeap->referenceOperations = NULL;
87 
88     if (!dvmCardTableStartup()) {
89         LOGE_HEAP("card table startup failed.");
90         return false;
91     }
92 
93     /* Initialize the HeapWorker locks and other state
94      * that the GC uses.
95      */
96     dvmInitializeHeapWorkerState();
97 
98     return true;
99 }
100 
dvmHeapStartupAfterZygote(void)101 bool dvmHeapStartupAfterZygote(void)
102 {
103     return dvmHeapSourceStartupAfterZygote();
104 }
105 
dvmHeapShutdown()106 void dvmHeapShutdown()
107 {
108 //TODO: make sure we're locked
109     if (gDvm.gcHeap != NULL) {
110         dvmCardTableShutdown();
111          /* Tables are allocated on the native heap; they need to be
112          * cleaned up explicitly.  The process may stick around, so we
113          * don't want to leak any native memory.
114          */
115         dvmHeapFreeLargeTable(gDvm.gcHeap->finalizableRefs);
116         gDvm.gcHeap->finalizableRefs = NULL;
117 
118         dvmHeapFreeLargeTable(gDvm.gcHeap->pendingFinalizationRefs);
119         gDvm.gcHeap->pendingFinalizationRefs = NULL;
120 
121         dvmHeapFreeLargeTable(gDvm.gcHeap->referenceOperations);
122         gDvm.gcHeap->referenceOperations = NULL;
123 
124         /* Destroy the heap.  Any outstanding pointers will point to
125          * unmapped memory (unless/until someone else maps it).  This
126          * frees gDvm.gcHeap as a side-effect.
127          */
128         dvmHeapSourceShutdown(&gDvm.gcHeap);
129     }
130 }
131 
132 /*
133  * Shutdown any threads internal to the heap.
134  */
dvmHeapThreadShutdown(void)135 void dvmHeapThreadShutdown(void)
136 {
137     dvmHeapSourceThreadShutdown();
138 }
139 
140 /*
141  * We've been asked to allocate something we can't, e.g. an array so
142  * large that (length * elementWidth) is larger than 2^31.
143  *
144  * _The Java Programming Language_, 4th edition, says, "you can be sure
145  * that all SoftReferences to softly reachable objects will be cleared
146  * before an OutOfMemoryError is thrown."
147  *
148  * It's unclear whether that holds for all situations where an OOM can
149  * be thrown, or just in the context of an allocation that fails due
150  * to lack of heap space.  For simplicity we just throw the exception.
151  *
152  * (OOM due to actually running out of space is handled elsewhere.)
153  */
dvmThrowBadAllocException(const char * msg)154 void dvmThrowBadAllocException(const char* msg)
155 {
156     dvmThrowException("Ljava/lang/OutOfMemoryError;", msg);
157 }
158 
159 /*
160  * Grab the lock, but put ourselves into THREAD_VMWAIT if it looks like
161  * we're going to have to wait on the mutex.
162  */
dvmLockHeap()163 bool dvmLockHeap()
164 {
165     if (dvmTryLockMutex(&gDvm.gcHeapLock) != 0) {
166         Thread *self;
167         ThreadStatus oldStatus;
168 
169         self = dvmThreadSelf();
170         oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
171         dvmLockMutex(&gDvm.gcHeapLock);
172         dvmChangeStatus(self, oldStatus);
173     }
174 
175     return true;
176 }
177 
dvmUnlockHeap()178 void dvmUnlockHeap()
179 {
180     dvmUnlockMutex(&gDvm.gcHeapLock);
181 }
182 
183 /* Pop an object from the list of pending finalizations and
184  * reference clears/enqueues, and return the object.
185  * The caller must call dvmReleaseTrackedAlloc()
186  * on the object when finished.
187  *
188  * Typically only called by the heap worker thread.
189  */
dvmGetNextHeapWorkerObject(HeapWorkerOperation * op)190 Object *dvmGetNextHeapWorkerObject(HeapWorkerOperation *op)
191 {
192     Object *obj;
193     GcHeap *gcHeap = gDvm.gcHeap;
194 
195     assert(op != NULL);
196 
197     dvmLockMutex(&gDvm.heapWorkerListLock);
198 
199     obj = dvmHeapGetNextObjectFromLargeTable(&gcHeap->referenceOperations);
200     if (obj != NULL) {
201         *op = WORKER_ENQUEUE;
202     } else {
203         obj = dvmHeapGetNextObjectFromLargeTable(
204                 &gcHeap->pendingFinalizationRefs);
205         if (obj != NULL) {
206             *op = WORKER_FINALIZE;
207         }
208     }
209 
210     if (obj != NULL) {
211         /* Don't let the GC collect the object until the
212          * worker thread is done with it.
213          */
214         dvmAddTrackedAlloc(obj, NULL);
215     }
216 
217     dvmUnlockMutex(&gDvm.heapWorkerListLock);
218 
219     return obj;
220 }
221 
222 /* Do a full garbage collection, which may grow the
223  * heap as a side-effect if the live set is large.
224  */
gcForMalloc(bool collectSoftReferences)225 static void gcForMalloc(bool collectSoftReferences)
226 {
227     if (gDvm.allocProf.enabled) {
228         Thread* self = dvmThreadSelf();
229         gDvm.allocProf.gcCount++;
230         if (self != NULL) {
231             self->allocProf.gcCount++;
232         }
233     }
234     /* This may adjust the soft limit as a side-effect.
235      */
236     LOGD_HEAP("dvmMalloc initiating GC%s\n",
237             collectSoftReferences ? "(collect SoftReferences)" : "");
238     dvmCollectGarbageInternal(collectSoftReferences, GC_FOR_MALLOC);
239 }
240 
241 /* Try as hard as possible to allocate some memory.
242  */
tryMalloc(size_t size)243 static void *tryMalloc(size_t size)
244 {
245     void *ptr;
246 
247     /* Don't try too hard if there's no way the allocation is
248      * going to succeed.  We have to collect SoftReferences before
249      * throwing an OOME, though.
250      */
251     if (size >= gDvm.heapSizeMax) {
252         LOGW_HEAP("dvmMalloc(%zu/0x%08zx): "
253                 "someone's allocating a huge buffer\n", size, size);
254         ptr = NULL;
255         goto collect_soft_refs;
256     }
257 
258 //TODO: figure out better heuristics
259 //    There will be a lot of churn if someone allocates a bunch of
260 //    big objects in a row, and we hit the frag case each time.
261 //    A full GC for each.
262 //    Maybe we grow the heap in bigger leaps
263 //    Maybe we skip the GC if the size is large and we did one recently
264 //      (number of allocations ago) (watch for thread effects)
265 //    DeflateTest allocs a bunch of ~128k buffers w/in 0-5 allocs of each other
266 //      (or, at least, there are only 0-5 objects swept each time)
267 
268     ptr = dvmHeapSourceAlloc(size);
269     if (ptr != NULL) {
270         return ptr;
271     }
272 
273     /*
274      * The allocation failed.  If the GC is running, block until it
275      * completes and retry.
276      */
277     if (gDvm.gcHeap->gcRunning) {
278         /*
279          * The GC is concurrently tracing the heap.  Release the heap
280          * lock, wait for the GC to complete, and retrying allocating.
281          */
282         dvmWaitForConcurrentGcToComplete();
283         ptr = dvmHeapSourceAlloc(size);
284         if (ptr != NULL) {
285             return ptr;
286         }
287     }
288     /*
289      * Another failure.  Our thread was starved or there may be too
290      * many live objects.  Try a foreground GC.  This will have no
291      * effect if the concurrent GC is already running.
292      */
293     gcForMalloc(false);
294     ptr = dvmHeapSourceAlloc(size);
295     if (ptr != NULL) {
296         return ptr;
297     }
298 
299     /* Even that didn't work;  this is an exceptional state.
300      * Try harder, growing the heap if necessary.
301      */
302     ptr = dvmHeapSourceAllocAndGrow(size);
303     if (ptr != NULL) {
304         size_t newHeapSize;
305 
306         newHeapSize = dvmHeapSourceGetIdealFootprint();
307 //TODO: may want to grow a little bit more so that the amount of free
308 //      space is equal to the old free space + the utilization slop for
309 //      the new allocation.
310         LOGI_HEAP("Grow heap (frag case) to "
311                 "%zu.%03zuMB for %zu-byte allocation\n",
312                 FRACTIONAL_MB(newHeapSize), size);
313         return ptr;
314     }
315 
316     /* Most allocations should have succeeded by now, so the heap
317      * is really full, really fragmented, or the requested size is
318      * really big.  Do another GC, collecting SoftReferences this
319      * time.  The VM spec requires that all SoftReferences have
320      * been collected and cleared before throwing an OOME.
321      */
322 //TODO: wait for the finalizers from the previous GC to finish
323 collect_soft_refs:
324     LOGI_HEAP("Forcing collection of SoftReferences for %zu-byte allocation\n",
325             size);
326     gcForMalloc(true);
327     ptr = dvmHeapSourceAllocAndGrow(size);
328     if (ptr != NULL) {
329         return ptr;
330     }
331 //TODO: maybe wait for finalizers and try one last time
332 
333     LOGE_HEAP("Out of memory on a %zd-byte allocation.\n", size);
334 //TODO: tell the HeapSource to dump its state
335     dvmDumpThread(dvmThreadSelf(), false);
336 
337     return NULL;
338 }
339 
340 /* Throw an OutOfMemoryError if there's a thread to attach it to.
341  * Avoid recursing.
342  *
343  * The caller must not be holding the heap lock, or else the allocations
344  * in dvmThrowException() will deadlock.
345  */
throwOOME()346 static void throwOOME()
347 {
348     Thread *self;
349 
350     if ((self = dvmThreadSelf()) != NULL) {
351         /* If the current (failing) dvmMalloc() happened as part of thread
352          * creation/attachment before the thread became part of the root set,
353          * we can't rely on the thread-local trackedAlloc table, so
354          * we can't keep track of a real allocated OOME object.  But, since
355          * the thread is in the process of being created, it won't have
356          * a useful stack anyway, so we may as well make things easier
357          * by throwing the (stackless) pre-built OOME.
358          */
359         if (dvmIsOnThreadList(self) && !self->throwingOOME) {
360             /* Let ourselves know that we tried to throw an OOM
361              * error in the normal way in case we run out of
362              * memory trying to allocate it inside dvmThrowException().
363              */
364             self->throwingOOME = true;
365 
366             /* Don't include a description string;
367              * one fewer allocation.
368              */
369             dvmThrowException("Ljava/lang/OutOfMemoryError;", NULL);
370         } else {
371             /*
372              * This thread has already tried to throw an OutOfMemoryError,
373              * which probably means that we're running out of memory
374              * while recursively trying to throw.
375              *
376              * To avoid any more allocation attempts, "throw" a pre-built
377              * OutOfMemoryError object (which won't have a useful stack trace).
378              *
379              * Note that since this call can't possibly allocate anything,
380              * we don't care about the state of self->throwingOOME
381              * (which will usually already be set).
382              */
383             dvmSetException(self, gDvm.outOfMemoryObj);
384         }
385         /* We're done with the possible recursion.
386          */
387         self->throwingOOME = false;
388     }
389 }
390 
391 /*
392  * Allocate storage on the GC heap.  We guarantee 8-byte alignment.
393  *
394  * The new storage is zeroed out.
395  *
396  * Note that, in rare cases, this could get called while a GC is in
397  * progress.  If a non-VM thread tries to attach itself through JNI,
398  * it will need to allocate some objects.  If this becomes annoying to
399  * deal with, we can block it at the source, but holding the allocation
400  * mutex should be enough.
401  *
402  * In rare circumstances (JNI AttachCurrentThread) we can be called
403  * from a non-VM thread.
404  *
405  * Use ALLOC_DONT_TRACK when we either don't want to track an allocation
406  * (because it's being done for the interpreter "new" operation and will
407  * be part of the root set immediately) or we can't (because this allocation
408  * is for a brand new thread).
409  *
410  * Returns NULL and throws an exception on failure.
411  *
412  * TODO: don't do a GC if the debugger thinks all threads are suspended
413  */
dvmMalloc(size_t size,int flags)414 void* dvmMalloc(size_t size, int flags)
415 {
416     GcHeap *gcHeap = gDvm.gcHeap;
417     void *ptr;
418 
419 #if defined(WITH_ALLOC_LIMITS)
420     /*
421      * See if they've exceeded the allocation limit for this thread.
422      *
423      * A limit value of -1 means "no limit".
424      *
425      * This is enabled at compile time because it requires us to do a
426      * TLS lookup for the Thread pointer.  This has enough of a performance
427      * impact that we don't want to do it if we don't have to.  (Now that
428      * we're using gDvm.checkAllocLimits we may want to reconsider this,
429      * but it's probably still best to just compile the check out of
430      * production code -- one less thing to hit on every allocation.)
431      */
432     if (gDvm.checkAllocLimits) {
433         Thread* self = dvmThreadSelf();
434         if (self != NULL) {
435             int count = self->allocLimit;
436             if (count > 0) {
437                 self->allocLimit--;
438             } else if (count == 0) {
439                 /* fail! */
440                 assert(!gDvm.initializing);
441                 self->allocLimit = -1;
442                 dvmThrowException("Ldalvik/system/AllocationLimitError;",
443                     "thread allocation limit exceeded");
444                 return NULL;
445             }
446         }
447     }
448 
449     if (gDvm.allocationLimit >= 0) {
450         assert(!gDvm.initializing);
451         gDvm.allocationLimit = -1;
452         dvmThrowException("Ldalvik/system/AllocationLimitError;",
453             "global allocation limit exceeded");
454         return NULL;
455     }
456 #endif
457 
458     dvmLockHeap();
459 
460     /* Try as hard as possible to allocate some memory.
461      */
462     ptr = tryMalloc(size);
463     if (ptr != NULL) {
464         /* We've got the memory.
465          */
466         if ((flags & ALLOC_FINALIZABLE) != 0) {
467             /* This object is an instance of a class that
468              * overrides finalize().  Add it to the finalizable list.
469              */
470             if (!dvmHeapAddRefToLargeTable(&gcHeap->finalizableRefs,
471                                     (Object *)ptr))
472             {
473                 LOGE_HEAP("dvmMalloc(): no room for any more "
474                         "finalizable objects\n");
475                 dvmAbort();
476             }
477         }
478 
479         if (gDvm.allocProf.enabled) {
480             Thread* self = dvmThreadSelf();
481             gDvm.allocProf.allocCount++;
482             gDvm.allocProf.allocSize += size;
483             if (self != NULL) {
484                 self->allocProf.allocCount++;
485                 self->allocProf.allocSize += size;
486             }
487         }
488     } else {
489         /* The allocation failed.
490          */
491 
492         if (gDvm.allocProf.enabled) {
493             Thread* self = dvmThreadSelf();
494             gDvm.allocProf.failedAllocCount++;
495             gDvm.allocProf.failedAllocSize += size;
496             if (self != NULL) {
497                 self->allocProf.failedAllocCount++;
498                 self->allocProf.failedAllocSize += size;
499             }
500         }
501     }
502 
503     dvmUnlockHeap();
504 
505     if (ptr != NULL) {
506         /*
507          * If caller hasn't asked us not to track it, add it to the
508          * internal tracking list.
509          */
510         if ((flags & ALLOC_DONT_TRACK) == 0) {
511             dvmAddTrackedAlloc(ptr, NULL);
512         }
513     } else {
514         /*
515          * The allocation failed; throw an OutOfMemoryError.
516          */
517         throwOOME();
518     }
519 
520     return ptr;
521 }
522 
523 /*
524  * Returns true iff <obj> points to a valid allocated object.
525  */
dvmIsValidObject(const Object * obj)526 bool dvmIsValidObject(const Object* obj)
527 {
528     /* Don't bother if it's NULL or not 8-byte aligned.
529      */
530     if (obj != NULL && ((uintptr_t)obj & (8-1)) == 0) {
531         /* Even if the heap isn't locked, this shouldn't return
532          * any false negatives.  The only mutation that could
533          * be happening is allocation, which means that another
534          * thread could be in the middle of a read-modify-write
535          * to add a new bit for a new object.  However, that
536          * RMW will have completed by the time any other thread
537          * could possibly see the new pointer, so there is no
538          * danger of dvmIsValidObject() being called on a valid
539          * pointer whose bit isn't set.
540          *
541          * Freeing will only happen during the sweep phase, which
542          * only happens while the heap is locked.
543          */
544         return dvmHeapSourceContains(obj);
545     }
546     return false;
547 }
548 
dvmObjectSizeInHeap(const Object * obj)549 size_t dvmObjectSizeInHeap(const Object *obj)
550 {
551     return dvmHeapSourceChunkSize(obj);
552 }
553 
verifyRootsAndHeap(void)554 static void verifyRootsAndHeap(void)
555 {
556     dvmVerifyRoots();
557     dvmVerifyBitmap(dvmHeapSourceGetLiveBits());
558 }
559 
560 /*
561  * Initiate garbage collection.
562  *
563  * NOTES:
564  * - If we don't hold gDvm.threadListLock, it's possible for a thread to
565  *   be added to the thread list while we work.  The thread should NOT
566  *   start executing, so this is only interesting when we start chasing
567  *   thread stacks.  (Before we do so, grab the lock.)
568  *
569  * We are not allowed to GC when the debugger has suspended the VM, which
570  * is awkward because debugger requests can cause allocations.  The easiest
571  * way to enforce this is to refuse to GC on an allocation made by the
572  * JDWP thread -- we have to expand the heap or fail.
573  */
dvmCollectGarbageInternal(bool clearSoftRefs,GcReason reason)574 void dvmCollectGarbageInternal(bool clearSoftRefs, GcReason reason)
575 {
576     GcHeap *gcHeap = gDvm.gcHeap;
577     u4 rootSuspend, rootSuspendTime, rootStart, rootEnd;
578     u4 dirtySuspend, dirtyStart, dirtyEnd;
579     u4 totalTime;
580     size_t numObjectsFreed, numBytesFreed;
581     size_t currAllocated, currFootprint;
582     size_t extAllocated, extLimit;
583     size_t percentFree;
584     GcMode gcMode;
585     int oldThreadPriority = kInvalidPriority;
586 
587     /* The heap lock must be held.
588      */
589 
590     if (gcHeap->gcRunning) {
591         LOGW_HEAP("Attempted recursive GC\n");
592         return;
593     }
594 
595     gcMode = (reason == GC_FOR_MALLOC) ? GC_PARTIAL : GC_FULL;
596     gcHeap->gcRunning = true;
597 
598     rootSuspend = dvmGetRelativeTimeMsec();
599     dvmSuspendAllThreads(SUSPEND_FOR_GC);
600     rootStart = dvmGetRelativeTimeMsec();
601     rootSuspendTime = rootStart - rootSuspend;
602 
603     /*
604      * If we are not marking concurrently raise the priority of the
605      * thread performing the garbage collection.
606      */
607     if (reason != GC_CONCURRENT) {
608         /* Get the priority (the "nice" value) of the current thread.  The
609          * getpriority() call can legitimately return -1, so we have to
610          * explicitly test errno.
611          */
612         errno = 0;
613         int priorityResult = getpriority(PRIO_PROCESS, 0);
614         if (errno != 0) {
615             LOGI_HEAP("getpriority(self) failed: %s\n", strerror(errno));
616         } else if (priorityResult > ANDROID_PRIORITY_NORMAL) {
617             /* Current value is numerically greater than "normal", which
618              * in backward UNIX terms means lower priority.
619              */
620 
621             if (priorityResult >= ANDROID_PRIORITY_BACKGROUND) {
622                 set_sched_policy(dvmGetSysThreadId(), SP_FOREGROUND);
623             }
624 
625             if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL) != 0) {
626                 LOGI_HEAP("Unable to elevate priority from %d to %d\n",
627                           priorityResult, ANDROID_PRIORITY_NORMAL);
628             } else {
629                 /* priority elevated; save value so we can restore it later */
630                 LOGD_HEAP("Elevating priority from %d to %d\n",
631                           priorityResult, ANDROID_PRIORITY_NORMAL);
632                 oldThreadPriority = priorityResult;
633             }
634         }
635     }
636 
637     /* Wait for the HeapWorker thread to block.
638      * (It may also already be suspended in interp code,
639      * in which case it's not holding heapWorkerLock.)
640      */
641     dvmLockMutex(&gDvm.heapWorkerLock);
642 
643     /* Make sure that the HeapWorker thread hasn't become
644      * wedged inside interp code.  If it has, this call will
645      * print a message and abort the VM.
646      */
647     dvmAssertHeapWorkerThreadRunning();
648 
649     /* Lock the pendingFinalizationRefs list.
650      *
651      * Acquire the lock after suspending so the finalizer
652      * thread can't block in the RUNNING state while
653      * we try to suspend.
654      */
655     dvmLockMutex(&gDvm.heapWorkerListLock);
656 
657     if (gDvm.preVerify) {
658         LOGV_HEAP("Verifying roots and heap before GC");
659         verifyRootsAndHeap();
660     }
661 
662     dvmMethodTraceGCBegin();
663 
664 #if WITH_HPROF
665 
666 /* Set DUMP_HEAP_ON_DDMS_UPDATE to 1 to enable heap dumps
667  * whenever DDMS requests a heap update (HPIF chunk).
668  * The output files will appear in /data/misc, which must
669  * already exist.
670  * You must define "WITH_HPROF := true" in your buildspec.mk
671  * and recompile libdvm for this to work.
672  *
673  * To enable stack traces for each allocation, define
674  * "WITH_HPROF_STACK := true" in buildspec.mk.  This option slows down
675  * allocations and also requires 8 additional bytes per object on the
676  * GC heap.
677  */
678 #define DUMP_HEAP_ON_DDMS_UPDATE 0
679 #if DUMP_HEAP_ON_DDMS_UPDATE
680     gcHeap->hprofDumpOnGc |= (gcHeap->ddmHpifWhen != 0);
681 #endif
682 
683     if (gcHeap->hprofDumpOnGc) {
684         char nameBuf[128];
685 
686         gcHeap->hprofResult = -1;
687 
688         if (gcHeap->hprofFileName == NULL) {
689             /* no filename was provided; invent one */
690             sprintf(nameBuf, "/data/misc/heap-dump-tm%d-pid%d.hprof",
691                 (int) time(NULL), (int) getpid());
692             gcHeap->hprofFileName = nameBuf;
693         }
694         gcHeap->hprofContext = hprofStartup(gcHeap->hprofFileName,
695                 gcHeap->hprofFd, gcHeap->hprofDirectToDdms);
696         if (gcHeap->hprofContext != NULL) {
697             hprofStartHeapDump(gcHeap->hprofContext);
698         }
699         gcHeap->hprofDumpOnGc = false;
700         gcHeap->hprofFileName = NULL;
701     }
702 #endif
703 
704     /* Set up the marking context.
705      */
706     if (!dvmHeapBeginMarkStep(gcMode)) {
707         LOGE_HEAP("dvmHeapBeginMarkStep failed; aborting\n");
708         dvmAbort();
709     }
710 
711     /* Mark the set of objects that are strongly reachable from the roots.
712      */
713     LOGD_HEAP("Marking...");
714     dvmHeapMarkRootSet();
715 
716     /* dvmHeapScanMarkedObjects() will build the lists of known
717      * instances of the Reference classes.
718      */
719     gcHeap->softReferences = NULL;
720     gcHeap->weakReferences = NULL;
721     gcHeap->phantomReferences = NULL;
722 
723     if (reason == GC_CONCURRENT) {
724         /*
725          * Resume threads while tracing from the roots.  We unlock the
726          * heap to allow mutator threads to allocate from free space.
727          */
728         rootEnd = dvmGetRelativeTimeMsec();
729         dvmClearCardTable();
730         dvmUnlockHeap();
731         dvmResumeAllThreads(SUSPEND_FOR_GC);
732     }
733 
734     /* Recursively mark any objects that marked objects point to strongly.
735      * If we're not collecting soft references, soft-reachable
736      * objects will also be marked.
737      */
738     LOGD_HEAP("Recursing...");
739     dvmHeapScanMarkedObjects();
740 
741     if (reason == GC_CONCURRENT) {
742         /*
743          * Re-acquire the heap lock and perform the final thread
744          * suspension.
745          */
746         dvmLockHeap();
747         dirtySuspend = dvmGetRelativeTimeMsec();
748         dvmSuspendAllThreads(SUSPEND_FOR_GC);
749         dirtyStart = dvmGetRelativeTimeMsec();
750         /*
751          * As no barrier intercepts root updates, we conservatively
752          * assume all roots may be gray and re-mark them.
753          */
754         dvmHeapReMarkRootSet();
755         /*
756          * With the exception of reference objects and weak interned
757          * strings, all gray objects should now be on dirty cards.
758          */
759         if (gDvm.verifyCardTable) {
760             dvmVerifyCardTable();
761         }
762         /*
763          * Recursively mark gray objects pointed to by the roots or by
764          * heap objects dirtied during the concurrent mark.
765          */
766         dvmHeapReScanMarkedObjects();
767     }
768 
769     /* All strongly-reachable objects have now been marked.
770      */
771     LOGD_HEAP("Handling soft references...");
772     if (!clearSoftRefs) {
773         dvmHandleSoftRefs(&gcHeap->softReferences);
774     }
775     dvmClearWhiteRefs(&gcHeap->softReferences);
776 
777     LOGD_HEAP("Handling weak references...");
778     dvmClearWhiteRefs(&gcHeap->weakReferences);
779 
780     /* Once all weak-reachable objects have been taken
781      * care of, any remaining unmarked objects can be finalized.
782      */
783     LOGD_HEAP("Finding finalizations...");
784     dvmHeapScheduleFinalizations();
785 
786     LOGD_HEAP("Handling f-reachable soft references...");
787     dvmClearWhiteRefs(&gcHeap->softReferences);
788 
789     LOGD_HEAP("Handling f-reachable weak references...");
790     dvmClearWhiteRefs(&gcHeap->weakReferences);
791 
792     /* Any remaining objects that are not pending finalization
793      * could be phantom-reachable.  This will mark any phantom-reachable
794      * objects, as well as enqueue their references.
795      */
796     LOGD_HEAP("Handling phantom references...");
797     dvmClearWhiteRefs(&gcHeap->phantomReferences);
798 
799 #if defined(WITH_JIT)
800     /*
801      * Patching a chaining cell is very cheap as it only updates 4 words. It's
802      * the overhead of stopping all threads and synchronizing the I/D cache
803      * that makes it expensive.
804      *
805      * Therefore we batch those work orders in a queue and go through them
806      * when threads are suspended for GC.
807      */
808     dvmCompilerPerformSafePointChecks();
809 #endif
810 
811     LOGD_HEAP("Sweeping...");
812 
813     dvmHeapSweepSystemWeaks();
814 
815     /*
816      * Live objects have a bit set in the mark bitmap, swap the mark
817      * and live bitmaps.  The sweep can proceed concurrently viewing
818      * the new live bitmap as the old mark bitmap, and vice versa.
819      */
820     dvmHeapSourceSwapBitmaps();
821 
822     if (gDvm.postVerify) {
823         LOGV_HEAP("Verifying roots and heap after GC");
824         verifyRootsAndHeap();
825     }
826 
827     if (reason == GC_CONCURRENT) {
828         dirtyEnd = dvmGetRelativeTimeMsec();
829         dvmUnlockHeap();
830         dvmResumeAllThreads(SUSPEND_FOR_GC);
831     }
832     dvmHeapSweepUnmarkedObjects(gcMode, reason == GC_CONCURRENT,
833                                 &numObjectsFreed, &numBytesFreed);
834     LOGD_HEAP("Cleaning up...");
835     dvmHeapFinishMarkStep();
836     if (reason == GC_CONCURRENT) {
837         dvmLockHeap();
838     }
839 
840     LOGD_HEAP("Done.");
841 
842     /* Now's a good time to adjust the heap size, since
843      * we know what our utilization is.
844      *
845      * This doesn't actually resize any memory;
846      * it just lets the heap grow more when necessary.
847      */
848     if (reason != GC_EXTERNAL_ALLOC) {
849         dvmHeapSourceGrowForUtilization();
850     }
851 
852     currAllocated = dvmHeapSourceGetValue(HS_BYTES_ALLOCATED, NULL, 0);
853     currFootprint = dvmHeapSourceGetValue(HS_FOOTPRINT, NULL, 0);
854 
855 #if WITH_HPROF
856     if (gcHeap->hprofContext != NULL) {
857         hprofFinishHeapDump(gcHeap->hprofContext);
858 //TODO: write a HEAP_SUMMARY record
859         if (hprofShutdown(gcHeap->hprofContext))
860             gcHeap->hprofResult = 0;    /* indicate success */
861         gcHeap->hprofContext = NULL;
862     }
863 #endif
864 
865     /* Now that we've freed up the GC heap, return any large
866      * free chunks back to the system.  They'll get paged back
867      * in the next time they're used.  Don't do it immediately,
868      * though;  if the process is still allocating a bunch of
869      * memory, we'll be taking a ton of page faults that we don't
870      * necessarily need to.
871      *
872      * Cancel any old scheduled trims, and schedule a new one.
873      */
874     dvmScheduleHeapSourceTrim(5);  // in seconds
875 
876     dvmMethodTraceGCEnd();
877     LOGV_HEAP("GC finished");
878 
879     gcHeap->gcRunning = false;
880 
881     LOGV_HEAP("Resuming threads");
882     dvmUnlockMutex(&gDvm.heapWorkerListLock);
883     dvmUnlockMutex(&gDvm.heapWorkerLock);
884 
885     if (reason == GC_CONCURRENT) {
886         /*
887          * Wake-up any threads that blocked after a failed allocation
888          * request.
889          */
890         dvmBroadcastCond(&gDvm.gcHeapCond);
891     }
892 
893     if (reason != GC_CONCURRENT) {
894         dirtyEnd = dvmGetRelativeTimeMsec();
895         dvmResumeAllThreads(SUSPEND_FOR_GC);
896         if (oldThreadPriority != kInvalidPriority) {
897             if (setpriority(PRIO_PROCESS, 0, oldThreadPriority) != 0) {
898                 LOGW_HEAP("Unable to reset priority to %d: %s\n",
899                           oldThreadPriority, strerror(errno));
900             } else {
901                 LOGD_HEAP("Reset priority to %d\n", oldThreadPriority);
902             }
903 
904             if (oldThreadPriority >= ANDROID_PRIORITY_BACKGROUND) {
905                 set_sched_policy(dvmGetSysThreadId(), SP_BACKGROUND);
906             }
907         }
908     }
909 
910     extAllocated = dvmHeapSourceGetValue(HS_EXTERNAL_BYTES_ALLOCATED, NULL, 0);
911     extLimit = dvmHeapSourceGetValue(HS_EXTERNAL_LIMIT, NULL, 0);
912     percentFree = 100 - (size_t)(100.0f * (float)currAllocated / currFootprint);
913     if (reason != GC_CONCURRENT) {
914         u4 markSweepTime = dirtyEnd - rootStart;
915         bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
916         totalTime = rootSuspendTime + markSweepTime;
917         LOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, external %zdK/%zdK, "
918              "paused %ums",
919              GcReasonStr[reason],
920              isSmall ? "<" : "",
921              numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
922              percentFree,
923              currAllocated / 1024, currFootprint / 1024,
924              extAllocated / 1024, extLimit / 1024,
925              markSweepTime);
926     } else {
927         u4 rootTime = rootEnd - rootStart;
928         u4 dirtySuspendTime = dirtyStart - dirtySuspend;
929         u4 dirtyTime = dirtyEnd - dirtyStart;
930         bool isSmall = numBytesFreed > 0 && numBytesFreed < 1024;
931         totalTime = rootSuspendTime + rootTime + dirtySuspendTime + dirtyTime;
932         LOGD("%s freed %s%zdK, %d%% free %zdK/%zdK, external %zdK/%zdK, "
933              "paused %ums+%ums",
934              GcReasonStr[reason],
935              isSmall ? "<" : "",
936              numBytesFreed ? MAX(numBytesFreed / 1024, 1) : 0,
937              percentFree,
938              currAllocated / 1024, currFootprint / 1024,
939              extAllocated / 1024, extLimit / 1024,
940              rootTime, dirtyTime);
941     }
942     dvmLogGcStats(numObjectsFreed, numBytesFreed, totalTime);
943     if (gcHeap->ddmHpifWhen != 0) {
944         LOGD_HEAP("Sending VM heap info to DDM\n");
945         dvmDdmSendHeapInfo(gcHeap->ddmHpifWhen, false);
946     }
947     if (gcHeap->ddmHpsgWhen != 0) {
948         LOGD_HEAP("Dumping VM heap to DDM\n");
949         dvmDdmSendHeapSegments(false, false);
950     }
951     if (gcHeap->ddmNhsgWhen != 0) {
952         LOGD_HEAP("Dumping native heap to DDM\n");
953         dvmDdmSendHeapSegments(false, true);
954     }
955 }
956 
dvmWaitForConcurrentGcToComplete(void)957 void dvmWaitForConcurrentGcToComplete(void)
958 {
959     Thread *self = dvmThreadSelf();
960     ThreadStatus oldStatus;
961     assert(self != NULL);
962     oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
963     dvmWaitCond(&gDvm.gcHeapCond, &gDvm.gcHeapLock);
964     dvmChangeStatus(self, oldStatus);
965 }
966 
967 #if WITH_HPROF
968 /*
969  * Perform garbage collection, writing heap information to the specified file.
970  *
971  * If "fd" is >= 0, the output will be written to that file descriptor.
972  * Otherwise, "fileName" is used to create an output file.
973  *
974  * If "fileName" is NULL, a suitable name will be generated automatically.
975  * (TODO: remove this when the SIGUSR1 feature goes away)
976  *
977  * If "directToDdms" is set, the other arguments are ignored, and data is
978  * sent directly to DDMS.
979  *
980  * Returns 0 on success, or an error code on failure.
981  */
hprofDumpHeap(const char * fileName,int fd,bool directToDdms)982 int hprofDumpHeap(const char* fileName, int fd, bool directToDdms)
983 {
984     int result;
985 
986     dvmLockMutex(&gDvm.gcHeapLock);
987 
988     gDvm.gcHeap->hprofDumpOnGc = true;
989     gDvm.gcHeap->hprofFileName = fileName;
990     gDvm.gcHeap->hprofFd = fd;
991     gDvm.gcHeap->hprofDirectToDdms = directToDdms;
992     dvmCollectGarbageInternal(false, GC_HPROF_DUMP_HEAP);
993     result = gDvm.gcHeap->hprofResult;
994 
995     dvmUnlockMutex(&gDvm.gcHeapLock);
996 
997     return result;
998 }
999 
dvmHeapSetHprofGcScanState(hprof_heap_tag_t state,u4 threadSerialNumber)1000 void dvmHeapSetHprofGcScanState(hprof_heap_tag_t state, u4 threadSerialNumber)
1001 {
1002     if (gDvm.gcHeap->hprofContext != NULL) {
1003         hprofSetGcScanState(gDvm.gcHeap->hprofContext, state,
1004                 threadSerialNumber);
1005     }
1006 }
1007 #endif
1008