- 浏览: 232363 次
- 性别:
- 来自: 北京
-
文章分类
最新评论
-
sfshine:
非常好非常好
Android 窗口管理 -
mthhk:
...
android3.0之Fragment(碎片)基础 -
FrankHB1989:
“C/C++标准不会保证这样的代码一定不会出错”的依据?你要说 ...
Duff's Device -
2006003845:
请问知道 刚体之间怎么不碰撞嘛 ?相互穿插
JBOX2D分析
gagbage collection:
为了跟踪对象的使用情况,必须知道内存中的对象是否在被使用,这就需要一个标志指示对象是否正在使用,也就是mark bits。一种方案是每个对象有自己相关联的mark bits。还有一种是将对象和mark bits分开,有独立的存放mark bits的内存区域。当你将mark bits 与对象一起存放的时候,就会消耗更多的缓冲区。当你将mark bits分开存放的时候,就能使缓冲区更紧密。这是从宏观上来看。
android平台上我们必须总要考虑到所有的进程都在一个性能受限的设备上。有各自独立的进程、每个进程有自己的堆,每个堆自己独立地进行垃圾收集。
GC和共享:
mark bits被分开存放。这在android上是可行的方案。这是因为有zygote进程在。Zygote有自己的堆,这个堆是被共享的,所以如果让mark bits散布在zygote堆中,当我们执行GC的时候,zygote进程就会触及zygote中的页面并使他们变为不共享的,使它们变为dirty的,就会破坏设备上的存储系统的性能。使mark bits被分开存放的另一个好处是因为在大多数情况下进程并不在垃圾收集,事实上我们并不需要分配mark数组除非是在垃圾收集的时候。也就是说我们就可以匀出内存运行更多的应用程序。
以上都是关于内存管理整体概况。在介绍内存管理代码之前先介绍java中关于引用对象的内容。
引用对象封装了指向其它对象的连接。被指向的对象称为引用目标。所有引用对象都是抽象的java.lang.Reference类的子类的实例。Reference类家族图如图所示,包含了三个直接的子类:SoftReference,WeakReference,PhantomReference。SoftReference对象封装了对引用目标的“软引用”;WeakReference封装了对引用目标的“弱引用”;而PhantomReference封装了对引用目标的“影子引用。强引用禁止引用目标被垃圾收集,而软引用、弱引用和影子引用不禁止。
图中表示了一个SoftReference对象。一旦一个引用对象创建后,它将一直维持到它的引用目标的软引用、弱引用或影子引用,直到它被程序或垃圾收集器清除。要清除一个引用对象,程序或垃圾收集器只需要调用引用对象的clear()方法。通过清除引用对象,就切断了引用对象的软引用、弱引用或者影子引用。
垃圾收集器可以随意更改不是强可触及的对象的可触及性状态。如果对可触及性状态的改变有兴趣,可以把引用对象和引用队列关联起来。引用队列是java.lang.ReferenceQueue的一个实例,垃圾收集器在改变可触及性状态时会添加(编入队列)所涉及的引用对象。如图所示,当垃圾收集决定收集弱可触及对象的时候,它会清除WeakReference对象(执行WeakRefence的clear方法),可能立即就把这个WeakReference对象加入到它的引用队列中,也可能在某个稍后的某个时间加入。为了把引用对象加入到它所关联的队列中,垃圾收集器执行它的enqueue方法。enqueue方法是在超类Reference中定义的,只有在创建引用对象时关联了一个队列、并且仅当对象的enqueue方法第一次执行时,才把引用对象加入到这个队列中。在不同的情况下,垃圾收集器把软引用、弱引用和影子引用对象加入队列表示三种不同的可触及性状态的转换。这表示六种可触及状态、状态变化的详情如下所示:
强可触及 对象可以从根节点不通过任何引用对象搜索到。对象生命周期从强可触及状态开始,并且只要有根节点或者另外一个强可触及对象引用它,就保持强可触及状态。垃圾收集器不会试图回收强可触及对象占据的内存空间。
软可触及 对象不是强可触及的,但是可以从根节点开始通过一个或多个(未被清除的)软引用对象触及。垃圾回收器可能回收软可触及的对象所占据的内存。如果发生了,它会清除所有到此软可触及对象的软引用。当垃圾收集器清除一个和引用队列有关联的软引用对象时,它把该软引用对象加入队列。
弱可触及 对象既不是强可触及的也不是软可触及的,但是从根节点开始可以通过一个或多个(未被清除的)弱引用对象触及。垃圾回收器必须归还弱可触及对象所占据的内存。这发生的时候,它会清除所有到此弱可触及对象的弱引用。当垃圾收集器清除一个和引用队列有关联的弱引用对象时,它把该弱引用对象加入队列。
可复活的 对象不是强可触及、软可触及,也不是弱可触及的,但是仍可能通过某些终结方法复活到这几种状态之一。
影子可触及 对象不是强可触及、软可触及,也不是弱可触及的,并且已经断定不会被任何终结方法复活(如果它自己定义了终结方法,它的终结方法已经被运行过了),并且它可以从根结点开始通过一个或多个(未被清除的)影子引用对象触及。一旦某个被影子引用的对象变成影子可触及状态,垃圾收集器立即把该引用对象加入队列。垃圾收集器从不会清除一个影子引用,所有的影子引用都必须由程序明确地清除。
不可触及 一个对象不是强可触及、弱可触及,也不是影子可触及,并且它不可复活。不可触及对象已经准备好被回收了。
垃圾收集器在把软引用对象和弱引用对象加入队列的时候,是在它们的引用目标离开相应的可触及状态时;而把影子引用对象加入队列是在引用目标进入相应的状态时!
看两段代码。这两段代码是垃圾回收中的能够说明引用对象使用的最好示例。
代码段1
代码段2
下面我们深入虚拟机中关于内存分配的代码。
首先介绍一下和内存管理有关的数据结构:
堆的数据结构:
typedef struct {
mspace *msp;//使用dlmalloc分配的内存,分配啊、释放啊就在这个对象上面
HeapBitmap objectBitmap;//位图信息1表示强可触及、0表示不是强可触及
size_t absoluteMaxSize;//堆可以增长到的最大值
size_t bytesAllocated;//已分配的字节数
size_t objectsAllocated;//已分配的对象数
} Heap;
struct HeapSource {
size_t argetUtilization;/*下的堆的使用率,取值范围为1..HEAP_UTILIZATION_MAX*/
size_t minimumSize;//分配的堆的最小大小
size_t startSize;//堆分配的初始大小
size_t absoluteMaxSize;//允许分配的对增长到的最大尺寸
size_t idealSize;//理想的堆的最大大小
size_t softLimit;//在垃圾收集前允许堆被分配的最大大小
Heap heaps[HEAP_SOURCE_MAX_HEAP_COUNT];//实实在在的堆
size_t numHeaps;//当前的堆的个数
size_t externalBytesAllocated;//不是归堆自己分配的外部分配的大小
size_t externalLimit;//允许外部分配的最大值
bool sawZygote;/*在创建这个HeapSource的时候是否是zygote模式也就是是否有zygote进程*/
};
typedef struct {
unsigned long int *bits;/*由mmap分配的全零初始化过的匿名内存区域*/
size_t bitsLen;//这个位图的大小
uintptr_t base;//位图对应的对象指针数组的首地址
uintptr_t max;/*位图使用中的最后一位被设置的对象指针的地址,如果位图全没设置则max<base*/
} HeapBitmap;
typedef struct {
/*从上往下增长的栈*/
const Object **limit;//允许增长到的最低地址
const Object **top;//栈顶
const Object **base;//栈底
} GcMarkStack;
typedef struct {
HeapBitmap bitmaps[HEAP_SOURCE_MAX_HEAP_COUNT];//存放位图的数组
size_t numBitmaps;//位图数
GcMarkStack stack;//GC标记栈
const void *finger;/*only used while scanning/recursing.存放地址上限的一个标记*/
} GcMarkContext;
struct GcHeap {
HeapSource *heapSource; //堆源数据结构,包含了所有的堆和和堆有关的信息
HeapRefTable nonCollectableRefs;//存储一些不可被垃圾回收的对象的参考表
LargeHeapRefTable *finalizableRefs;/*存储一些当被垃圾回收时需要执行finalize方法的参考表*/
LargeHeapRefTable *pendingFinalizationRefs;/*存储一些需要执行finalize方法的对象的参考表,但是我没有看到在代码里何处往这个表中填入对象*/
Object *softReferences;//软引用对象的列表
Object *weakReferences;//弱引用对象的列表
Object *phantomReferences;//影子引用对象的列表
LargeHeapRefTable *referenceOperations;/*需要被执行clear或者enqueue方法的引用对象的列表*/
Object *heapWorkerCurrentObject;
Method *heapWorkerCurrentMethod;/*如果这两个对象不为空则表示HeapWorker线程正在执行*/
u8 heapWorkerInterpStartTime;/*如果heapWorkerCurrentObject非空则表示HeapWorker开始执行这个方法的时间*/
u8 heapWorkerInterpCpuStartTime; /*如果heapWorkerCurrentObject非空则表示HeapWorker CPU开始执行这个方法的时间*/
struct timespec heapWorkerNextTrim;/*下一次裁剪Heap Source的时间*/
GcMarkContext markContext;//做标记步骤中的状态
u8 gcStartTime;//GC开始的时间
bool gcRunning;//是否正在GC
enum{SR_COLLECT_NONE,SR_COLLECT_SOME,SR_COLLECT_ALL} softReferenceCollectionState;//GC时软引用对象回收多少,一点都不收集?回收一半?还是全部回收?
size_t softReferenceHeapSizeThreshold;/*存在多少软引用对象的时候开始回收软引用对象*/
int softReferenceColor;/*当软引用回收策略为回收一半时使用的概率值*/
bool markAllReferents;/*如果被设为true则任何软/弱/影子引用对象引用的对象都会被标记,如果设为false则采用普通的引用收集策略*/
下面都是统计调试跟踪用到的变量不去管它们
#if DVM_TRACK_HEAP_MARKING
size_t markCount;
size_t markSize;
#endif
int ddmHpifWhen;
int ddmHpsgWhen;
int ddmHpsgWhat;
int ddmNhsgWhen;
int ddmNhsgWhat;
#if WITH_HPROF
bool hprofDumpOnGc;
hprof_context_t *hprofContext;
#endif
};
Dalvik virtual machine的与内存管理相关的数据结构关系如图所示。为什么堆只有三个我想是因为初始化的时候生成的是一个堆,如果有Zygote进程则生成的时候又生成一个堆,再从Zygote进程fork新的进程的时候又生成一个堆,之后进程就永远对这个堆也就是被称为是当前堆进行操作,不会再生成新的堆了。
首先看看HeapWorker线程。它主要做执行对象的终结函数和引用对象的清理和归队工作,下面看它的代码。
static void doHeapWork(Thread *self)
{
Object *obj;
HeapWorkerOperation op;
int numFinalizersCalled, numReferencesEnqueued;
numFinalizersCalled = 0;
numReferencesEnqueued = 0;
while ((obj = dvmGetNextHeapWorkerObject(&op)) != NULL) {
Method *method = NULL;
/* Make sure the object hasn't been collected since
* being scheduled.
*/
/* Call the appropriate method(s).
*/
if (op == WORKER_FINALIZE) {
numFinalizersCalled++;
method = obj->clazz->vtable[gDvm.voffJavaLangObject_finalize];
callMethod(self, obj, method);
} else {
if (op & WORKER_ENQUEUE) {
numReferencesEnqueued++;
callMethod(self, obj,
gDvm.methJavaLangRefReference_enqueueInternal);
}
}
/* Let the GC collect the object.
*/
dvmReleaseTrackedAlloc(obj, self);
}
}
HeapWorker线程的主函数:
static void* heapWorkerThreadStart(void* arg)
{
Thread *self = dvmThreadSelf();
int cc;
/* tell the main thread that we're ready */
dvmLockMutex(&gDvm.heapWorkerLock);
gDvm.heapWorkerReady = true;
cc = pthread_cond_signal(&gDvm.heapWorkerCond);
dvmUnlockMutex(&gDvm.heapWorkerLock);
dvmLockMutex(&gDvm.heapWorkerLock);
while (!gDvm.haltHeapWorker) {
struct timespec trimtime;
bool timedwait = false;
/* We're done running interpreted code for now. */
dvmChangeStatus(NULL, THREAD_VMWAIT);
/* Signal anyone who wants to know when we're done. */
cc = pthread_cond_broadcast(&gDvm.heapWorkerIdleCond);
/* Trim the heap if we were asked to. */
trimtime = gDvm.gcHeap->heapWorkerNextTrim;
if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) {
struct timeval now;
gettimeofday(&now, NULL);
if (trimtime.tv_sec < now.tv_sec ||
(trimtime.tv_sec == now.tv_sec &&
trimtime.tv_nsec <= now.tv_usec * 1000))
{
size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT];
/* The heap must be locked before the HeapWorker;
* unroll and re-order the locks. dvmLockHeap()
* will put us in VMWAIT if necessary. Once it
* returns, there shouldn't be any contention on
* heapWorkerLock.
*/
dvmUnlockMutex(&gDvm.heapWorkerLock);
dvmLockHeap();
dvmLockMutex(&gDvm.heapWorkerLock);
memset(madvisedSizes, 0, sizeof(madvisedSizes));
dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
dvmLogMadviseStats(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
dvmUnlockHeap();
trimtime.tv_sec = 0;
trimtime.tv_nsec = 0;
gDvm.gcHeap->heapWorkerNextTrim = trimtime;
} else {
timedwait = true;
}
}
/* sleep until signaled */
if (timedwait) {
cc = pthread_cond_timedwait(&gDvm.heapWorkerCond,
&gDvm.heapWorkerLock, &trimtime);
} else {
cc = pthread_cond_wait(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
}
/* dvmChangeStatus() may block; don't hold heapWorkerLock.
*/
dvmUnlockMutex(&gDvm.heapWorkerLock);
dvmChangeStatus(NULL, THREAD_RUNNING);
dvmLockMutex(&gDvm.heapWorkerLock);
/* Process any events in the queue.
*/
doHeapWork(self);
}
dvmUnlockMutex(&gDvm.heapWorkerLock);
return NULL;
}
下面是显示GC收集的主函数。
void dvmCollectGarbageInternal(bool collectSoftReferences)
{
GcHeap *gcHeap = gDvm.gcHeap;
Object *softReferences;
Object *weakReferences;
Object *phantomReferences;
u8 now;
s8 timeSinceLastGc;
s8 gcElapsedTime;
int numFreed;
size_t sizeFreed;
/* The heap lock must be held.
*/
//先初始化一些变量
if (gcHeap->gcRunning) {
return;
}//如果GC已经在运行则退出
//......设置时间
dvmSuspendAllThreads(SUSPEND_FOR_GC);//挂起所有的线程
/* Get the priority (the "nice" value) of the current thread. The
* getpriority() call can legitimately return -1, so we have to
* explicitly test errno.
*/
//......获取当前线程的优先级
oldThreadPriority = priorityResult;
dvmLockMutex(&gDvm.heapWorkerLock);
/* Make sure that the HeapWorker thread hasn't become
* wedged inside interp code. If it has, this call will
* print a message and abort the VM.
*/
dvmAssertHeapWorkerThreadRunning();
这里插入dvmAssertHeapWorkerThreadRunning的代码看一下。
/* Make sure that the HeapWorker thread hasn't spent an inordinate
* amount of time inside interpreted a finalizer.
*
* Aborts the VM if the thread appears to be wedged.
*
* The caller must hold the heapWorkerLock to guarantee an atomic
* read of the watchdog values.
*/
void dvmAssertHeapWorkerThreadRunning()
{
if (gDvm.gcHeap->heapWorkerCurrentObject != NULL) {
static const u8 HEAP_WORKER_WATCHDOG_TIMEOUT = 10*1000*1000LL; // 10sec
u8 heapWorkerInterpStartTime = gDvm.gcHeap->heapWorkerInterpStartTime;
u8 now = dvmGetRelativeTimeUsec();
u8 delta = now - heapWorkerInterpStartTime;
u8 heapWorkerInterpCpuStartTime =
gDvm.gcHeap->heapWorkerInterpCpuStartTime;
u8 nowCpu = dvmGetOtherThreadCpuTimeUsec(gDvm.heapWorkerHandle);
u8 deltaCpu = nowCpu - heapWorkerInterpCpuStartTime;
if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT && gDvm.debuggerActive) {
/*
* Debugger suspension can block the thread indefinitely. For
* best results we should reset this explicitly whenever the
* HeapWorker thread is resumed. Ignoring the yelp isn't
* quite right but will do for a quick fix.
*/
LOGI("Debugger is attached -- suppressing HeapWorker watchdog/n");
heapWorkerInterpStartTime = now; /* reset timer */
} else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT) {
char* desc = dexProtoCopyMethodDescriptor(
&gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
LOGE("HeapWorker is wedged: %lldms spent inside %s.%s%s/n",
delta / 1000,
gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
free(desc);
dvmDumpAllThreads(true);
/* abort the VM */
dvmAbort();
} else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT / 2) {
char* desc = dexProtoCopyMethodDescriptor(
&gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
LOGW("HeapWorker may be wedged: %lldms spent inside %s.%s%s/n",
delta / 1000,
gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
free(desc);
}
}
}
继续
/* Lock the pendingFinalizationRefs list.
*
* Acquire the lock after suspending so the finalizer
* thread can't block in the RUNNING state while
* we try to suspend.
*/
dvmLockMutex(&gDvm.heapWorkerListLock);
/* Set up the marking context.
*/
dvmHeapBeginMarkStep();
/* Mark the set of objects that are strongly reachable from the roots.
*/
dvmHeapMarkRootSet();
/* dvmHeapScanMarkedObjects() will build the lists of known
* instances of the Reference classes.
*/
gcHeap->softReferences = NULL;
gcHeap->weakReferences = NULL;
gcHeap->phantomReferences = NULL;
/* Make sure that we don't hard-mark the referents of Reference
* objects by default.
*/
gcHeap->markAllReferents = false;
/* Don't mark SoftReferences if our caller wants us to collect them.
* This has to be set before calling dvmHeapScanMarkedObjects().
*/
if (collectSoftReferences) {
gcHeap->softReferenceCollectionState = SR_COLLECT_ALL;
}
/* Recursively mark any objects that marked objects point to strongly.
* If we're not collecting soft references, soft-reachable
* objects will also be marked.
*/
dvmHeapScanMarkedObjects();
这里插入的代码看一下。
void dvmHeapScanMarkedObjects()
{
GcMarkContext *ctx = &gDvm.gcHeap->markContext;
/* The bitmaps currently have bits set for the root set.
* Walk across the bitmaps and scan each object.
*/
#ifndef NDEBUG
gLastFinger = 0;
#endif
dvmHeapBitmapWalkList(ctx->bitmaps, ctx->numBitmaps,
scanBitmapCallback, ctx);
/* We've walked the mark bitmaps. Scan anything that's
* left on the mark stack.
*/
processMarkStack(ctx);
}
这里插入processMarkStack的代码看一下。
static void processMarkStack(GcMarkContext *ctx)
{
const Object **const base = ctx->stack.base;
/* Scan anything that's on the mark stack.
* We can't use the bitmaps anymore, so use
* a finger that points past the end of them.
*/
ctx->finger = (void *)ULONG_MAX;
while (ctx->stack.top != base) {
scanObject(*ctx->stack.top++, ctx);
}
}
其中scanObject是一个复杂的函数,它调用了更为复杂的多个函数,太长太复杂了,在这里不便列出。其实最重要的一点是它之中还是会操作ctx所指向的栈,将新的对象加入栈顶,到下一次回到这个循环的时候,ctx->stack.top实际上又在栈顶了。如此下次实际上就代替深度优先搜索把所有的强可触及对象都给标记了。
继续
/* Latch these so that the other calls to dvmHeapScanMarkedObjects() don't
* mess with them.
*/
softReferences = gcHeap->softReferences;
weakReferences = gcHeap->weakReferences;
phantomReferences = gcHeap->phantomReferences;
/* All strongly-reachable objects have now been marked.
*/
if (gcHeap->softReferenceCollectionState != SR_COLLECT_NONE) {
dvmHeapHandleReferences(softReferences, REF_SOFT);
// markCount always zero
/* Now that we've tried collecting SoftReferences,
* fall back to not collecting them. If the heap
* grows, we will start collecting again.
*/
gcHeap->softReferenceCollectionState = SR_COLLECT_NONE;
} // else dvmHeapScanMarkedObjects() already marked the soft-reachable set
dvmHeapHandleReferences(weakReferences, REF_WEAK);
// markCount always zero
/* Once all weak-reachable objects have been taken
* care of, any remaining unmarked objects can be finalized.
*/
dvmHeapScheduleFinalizations();
/* Any remaining objects that are not pending finalization
* could be phantom-reachable. This will mark any phantom-reachable
* objects, as well as enqueue their references.
*/
dvmHeapHandleReferences(phantomReferences, REF_PHANTOM);
在以上这段代码中其实又多次调用了processMarkStack方法,把引用对象相关的强可触及对象又都标记上了。
dvmHeapSweepUnmarkedObjects(&numFreed, &sizeFreed);
dvmHeapFinishMarkStep();
/* Now's a good time to adjust the heap size, since
* we know what our utilization is.
*
* This doesn't actually resize any memory;
* it just lets the heap grow more when necessary.
*/
dvmHeapSourceGrowForUtilization();
dvmHeapSizeChanged();
/* Now that we've freed up the GC heap, return any large
* free chunks back to the system. They'll get paged back
* in the next time they're used. Don't do it immediately,
* though; if the process is still allocating a bunch of
* memory, we'll be taking a ton of page faults that we don't
* necessarily need to.
*
* Cancel any old scheduled trims, and schedule a new one.
*/
dvmScheduleHeapSourceTrim(5); // in seconds
gcHeap->gcRunning = false;
dvmUnlockMutex(&gDvm.heapWorkerListLock);
dvmUnlockMutex(&gDvm.heapWorkerLock);
dvmResumeAllThreads(SUSPEND_FOR_GC);
//......其它一些事情
}
dvmHeapSweepUnmarkedObjects又是一个巨复杂的函数,其中会调用到的释放堆中的对象的函数是dvmHeapSourceFree,可以看看这个函数。
void dvmHeapSourceFree(void *ptr)
{
Heap *heap;
HS_BOILERPLATE();
heap = ptr2heap(gHs, ptr);
if (heap != NULL) {
countFree(heap, ptr, true);
/* Only free objects that are in the active heap.
* Touching old heaps would pull pages into this process.
*/
if (heap == gHs->heaps) {
mspace_free(heap->msp, ptr);
}
}
}
其中的mspace_free函数就是真正的堆空间的释放和压缩,但是这一已经是dlmalloc的部分不是属于虚拟机的内存管理了。
Dalvik virtual Machine的初始化代码里有GC的初始化。
int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized,
JNIEnv* pEnv)
{
//......
if (!dvmGcStartup())
goto fail;
//......
}
bool dvmGcStartup(void)
{
dvmInitMutex(&gDvm.gcHeapLock);
return dvmHeapStartup();
}
bool dvmHeapStartup()
{
GcHeap *gcHeap;
#if defined(WITH_ALLOC_LIMITS)
gDvm.checkAllocLimits = false;
gDvm.allocationLimit = -1;
#endif
gcHeap = dvmHeapSourceStartup(gDvm.heapSizeStart, gDvm.heapSizeMax);
if (gcHeap == NULL) {
return false;
}
gcHeap->heapWorkerCurrentObject = NULL;
gcHeap->heapWorkerCurrentMethod = NULL;
gcHeap->heapWorkerInterpStartTime = 0LL;
gcHeap->softReferenceCollectionState = SR_COLLECT_NONE;
gcHeap->softReferenceHeapSizeThreshold = gDvm.heapSizeStart;
gcHeap->ddmHpifWhen = 0;
gcHeap->ddmHpsgWhen = 0;
gcHeap->ddmHpsgWhat = 0;
gcHeap->ddmNhsgWhen = 0;
gcHeap->ddmNhsgWhat = 0;
#if WITH_HPROF
gcHeap->hprofDumpOnGc = false;
gcHeap->hprofContext = NULL;
#endif
/* This needs to be set before we call dvmHeapInitHeapRefTable().
*/
gDvm.gcHeap = gcHeap;
/* Set up the table we'll use for ALLOC_NO_GC.
*/
if (!dvmHeapInitHeapRefTable(&gcHeap->nonCollectableRefs,
kNonCollectableRefDefault))
{
LOGE_HEAP("Can't allocate GC_NO_ALLOC table/n");
goto fail;
}
/* Set up the lists and lock we'll use for finalizable
* and reference objects.
*/
dvmInitMutex(&gDvm.heapWorkerListLock);
gcHeap->finalizableRefs = NULL;
gcHeap->pendingFinalizationRefs = NULL;
gcHeap->referenceOperations = NULL;
/* Initialize the HeapWorker locks and other state
* that the GC uses.
*/
dvmInitializeHeapWorkerState();
return true;
fail:
gDvm.gcHeap = NULL;
dvmHeapSourceShutdown(gcHeap);
return false;
}
GcHeap *
dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize)
{
GcHeap *gcHeap;
HeapSource *hs;
Heap *heap;
mspace msp;
assert(gHs == NULL);
if (startSize > absoluteMaxSize) {
LOGE("Bad heap parameters (start=%d, max=%d)/n",
startSize, absoluteMaxSize);
return NULL;
}
/* Create an unlocked dlmalloc mspace to use as
* the small object heap source.
*/
msp = createMspace(startSize, absoluteMaxSize, 0);
if (msp == NULL) {
return false;
}
/* Allocate a descriptor from the heap we just created.
*/
gcHeap = mspace_malloc(msp, sizeof(*gcHeap));
if (gcHeap == NULL) {
LOGE_HEAP("Can't allocate heap descriptor/n");
goto fail;
}
memset(gcHeap, 0, sizeof(*gcHeap));
hs = mspace_malloc(msp, sizeof(*hs));
if (hs == NULL) {
LOGE_HEAP("Can't allocate heap source/n");
goto fail;
}
memset(hs, 0, sizeof(*hs));
hs->targetUtilization = DEFAULT_HEAP_UTILIZATION;
hs->minimumSize = 0;
hs->startSize = startSize;
hs->absoluteMaxSize = absoluteMaxSize;
hs->idealSize = startSize;
hs->softLimit = INT_MAX; // no soft limit at first
hs->numHeaps = 0;
hs->sawZygote = gDvm.zygote;
if (!addNewHeap(hs, msp, absoluteMaxSize)) {
LOGE_HEAP("Can't add initial heap/n");
goto fail;
}
gcHeap->heapSource = hs;
countAllocation(hs2heap(hs), gcHeap, false);
countAllocation(hs2heap(hs), hs, false);
gHs = hs;
return gcHeap;
fail:
destroy_contiguous_mspace(msp);
return NULL;
}
完成分配以后,内存结构如图所示。
然后看关闭的过程
void dvmGcShutdown(void)
{
//TODO: grab and destroy the lock
dvmHeapShutdown();
}
void dvmHeapShutdown()
{
//TODO: make sure we're locked
if (gDvm.gcHeap != NULL) {
GcHeap *gcHeap;
gcHeap = gDvm.gcHeap;
gDvm.gcHeap = NULL;
/* Tables are allocated on the native heap;
* they need to be cleaned up explicitly.
* The process may stick around, so we don't
* want to leak any native memory.
*/
dvmHeapFreeHeapRefTable(&gcHeap->nonCollectableRefs);
dvmHeapFreeLargeTable(gcHeap->finalizableRefs);
gcHeap->finalizableRefs = NULL;
dvmHeapFreeLargeTable(gcHeap->pendingFinalizationRefs);
gcHeap->pendingFinalizationRefs = NULL;
dvmHeapFreeLargeTable(gcHeap->referenceOperations);
gcHeap->referenceOperations = NULL;
/* Destroy the heap. Any outstanding pointers
* will point to unmapped memory (unless/until
* someone else maps it). This frees gcHeap
* as a side-effect.
*/
dvmHeapSourceShutdown(gcHeap);
}
}
void dvmHeapSourceShutdown(GcHeap *gcHeap)
{
if (gcHeap != NULL && gcHeap->heapSource != NULL) {
HeapSource *hs;
size_t numHeaps;
size_t i;
hs = gcHeap->heapSource;
gHs = NULL;
/* Cache numHeaps because hs will be invalid after the last
* heap is freed.
*/
numHeaps = hs->numHeaps;
for (i = 0; i < numHeaps; i++) {
Heap *heap = &hs->heaps[i];
dvmHeapBitmapDelete(&heap->objectBitmap);
destroy_contiguous_mspace(heap->msp);
}
/* The last heap is the original one, which contains the
* HeapSource object itself.
*/
}
}
为了跟踪对象的使用情况,必须知道内存中的对象是否在被使用,这就需要一个标志指示对象是否正在使用,也就是mark bits。一种方案是每个对象有自己相关联的mark bits。还有一种是将对象和mark bits分开,有独立的存放mark bits的内存区域。当你将mark bits 与对象一起存放的时候,就会消耗更多的缓冲区。当你将mark bits分开存放的时候,就能使缓冲区更紧密。这是从宏观上来看。
android平台上我们必须总要考虑到所有的进程都在一个性能受限的设备上。有各自独立的进程、每个进程有自己的堆,每个堆自己独立地进行垃圾收集。
GC和共享:
mark bits被分开存放。这在android上是可行的方案。这是因为有zygote进程在。Zygote有自己的堆,这个堆是被共享的,所以如果让mark bits散布在zygote堆中,当我们执行GC的时候,zygote进程就会触及zygote中的页面并使他们变为不共享的,使它们变为dirty的,就会破坏设备上的存储系统的性能。使mark bits被分开存放的另一个好处是因为在大多数情况下进程并不在垃圾收集,事实上我们并不需要分配mark数组除非是在垃圾收集的时候。也就是说我们就可以匀出内存运行更多的应用程序。
以上都是关于内存管理整体概况。在介绍内存管理代码之前先介绍java中关于引用对象的内容。
引用对象封装了指向其它对象的连接。被指向的对象称为引用目标。所有引用对象都是抽象的java.lang.Reference类的子类的实例。Reference类家族图如图所示,包含了三个直接的子类:SoftReference,WeakReference,PhantomReference。SoftReference对象封装了对引用目标的“软引用”;WeakReference封装了对引用目标的“弱引用”;而PhantomReference封装了对引用目标的“影子引用。强引用禁止引用目标被垃圾收集,而软引用、弱引用和影子引用不禁止。
图中表示了一个SoftReference对象。一旦一个引用对象创建后,它将一直维持到它的引用目标的软引用、弱引用或影子引用,直到它被程序或垃圾收集器清除。要清除一个引用对象,程序或垃圾收集器只需要调用引用对象的clear()方法。通过清除引用对象,就切断了引用对象的软引用、弱引用或者影子引用。
垃圾收集器可以随意更改不是强可触及的对象的可触及性状态。如果对可触及性状态的改变有兴趣,可以把引用对象和引用队列关联起来。引用队列是java.lang.ReferenceQueue的一个实例,垃圾收集器在改变可触及性状态时会添加(编入队列)所涉及的引用对象。如图所示,当垃圾收集决定收集弱可触及对象的时候,它会清除WeakReference对象(执行WeakRefence的clear方法),可能立即就把这个WeakReference对象加入到它的引用队列中,也可能在某个稍后的某个时间加入。为了把引用对象加入到它所关联的队列中,垃圾收集器执行它的enqueue方法。enqueue方法是在超类Reference中定义的,只有在创建引用对象时关联了一个队列、并且仅当对象的enqueue方法第一次执行时,才把引用对象加入到这个队列中。在不同的情况下,垃圾收集器把软引用、弱引用和影子引用对象加入队列表示三种不同的可触及性状态的转换。这表示六种可触及状态、状态变化的详情如下所示:
强可触及 对象可以从根节点不通过任何引用对象搜索到。对象生命周期从强可触及状态开始,并且只要有根节点或者另外一个强可触及对象引用它,就保持强可触及状态。垃圾收集器不会试图回收强可触及对象占据的内存空间。
软可触及 对象不是强可触及的,但是可以从根节点开始通过一个或多个(未被清除的)软引用对象触及。垃圾回收器可能回收软可触及的对象所占据的内存。如果发生了,它会清除所有到此软可触及对象的软引用。当垃圾收集器清除一个和引用队列有关联的软引用对象时,它把该软引用对象加入队列。
弱可触及 对象既不是强可触及的也不是软可触及的,但是从根节点开始可以通过一个或多个(未被清除的)弱引用对象触及。垃圾回收器必须归还弱可触及对象所占据的内存。这发生的时候,它会清除所有到此弱可触及对象的弱引用。当垃圾收集器清除一个和引用队列有关联的弱引用对象时,它把该弱引用对象加入队列。
可复活的 对象不是强可触及、软可触及,也不是弱可触及的,但是仍可能通过某些终结方法复活到这几种状态之一。
影子可触及 对象不是强可触及、软可触及,也不是弱可触及的,并且已经断定不会被任何终结方法复活(如果它自己定义了终结方法,它的终结方法已经被运行过了),并且它可以从根结点开始通过一个或多个(未被清除的)影子引用对象触及。一旦某个被影子引用的对象变成影子可触及状态,垃圾收集器立即把该引用对象加入队列。垃圾收集器从不会清除一个影子引用,所有的影子引用都必须由程序明确地清除。
不可触及 一个对象不是强可触及、弱可触及,也不是影子可触及,并且它不可复活。不可触及对象已经准备好被回收了。
垃圾收集器在把软引用对象和弱引用对象加入队列的时候,是在它们的引用目标离开相应的可触及状态时;而把影子引用对象加入队列是在引用目标进入相应的状态时!
看两段代码。这两段代码是垃圾回收中的能够说明引用对象使用的最好示例。
代码段1
代码段2
下面我们深入虚拟机中关于内存分配的代码。
首先介绍一下和内存管理有关的数据结构:
堆的数据结构:
typedef struct {
mspace *msp;//使用dlmalloc分配的内存,分配啊、释放啊就在这个对象上面
HeapBitmap objectBitmap;//位图信息1表示强可触及、0表示不是强可触及
size_t absoluteMaxSize;//堆可以增长到的最大值
size_t bytesAllocated;//已分配的字节数
size_t objectsAllocated;//已分配的对象数
} Heap;
struct HeapSource {
size_t argetUtilization;/*下的堆的使用率,取值范围为1..HEAP_UTILIZATION_MAX*/
size_t minimumSize;//分配的堆的最小大小
size_t startSize;//堆分配的初始大小
size_t absoluteMaxSize;//允许分配的对增长到的最大尺寸
size_t idealSize;//理想的堆的最大大小
size_t softLimit;//在垃圾收集前允许堆被分配的最大大小
Heap heaps[HEAP_SOURCE_MAX_HEAP_COUNT];//实实在在的堆
size_t numHeaps;//当前的堆的个数
size_t externalBytesAllocated;//不是归堆自己分配的外部分配的大小
size_t externalLimit;//允许外部分配的最大值
bool sawZygote;/*在创建这个HeapSource的时候是否是zygote模式也就是是否有zygote进程*/
};
typedef struct {
unsigned long int *bits;/*由mmap分配的全零初始化过的匿名内存区域*/
size_t bitsLen;//这个位图的大小
uintptr_t base;//位图对应的对象指针数组的首地址
uintptr_t max;/*位图使用中的最后一位被设置的对象指针的地址,如果位图全没设置则max<base*/
} HeapBitmap;
typedef struct {
/*从上往下增长的栈*/
const Object **limit;//允许增长到的最低地址
const Object **top;//栈顶
const Object **base;//栈底
} GcMarkStack;
typedef struct {
HeapBitmap bitmaps[HEAP_SOURCE_MAX_HEAP_COUNT];//存放位图的数组
size_t numBitmaps;//位图数
GcMarkStack stack;//GC标记栈
const void *finger;/*only used while scanning/recursing.存放地址上限的一个标记*/
} GcMarkContext;
struct GcHeap {
HeapSource *heapSource; //堆源数据结构,包含了所有的堆和和堆有关的信息
HeapRefTable nonCollectableRefs;//存储一些不可被垃圾回收的对象的参考表
LargeHeapRefTable *finalizableRefs;/*存储一些当被垃圾回收时需要执行finalize方法的参考表*/
LargeHeapRefTable *pendingFinalizationRefs;/*存储一些需要执行finalize方法的对象的参考表,但是我没有看到在代码里何处往这个表中填入对象*/
Object *softReferences;//软引用对象的列表
Object *weakReferences;//弱引用对象的列表
Object *phantomReferences;//影子引用对象的列表
LargeHeapRefTable *referenceOperations;/*需要被执行clear或者enqueue方法的引用对象的列表*/
Object *heapWorkerCurrentObject;
Method *heapWorkerCurrentMethod;/*如果这两个对象不为空则表示HeapWorker线程正在执行*/
u8 heapWorkerInterpStartTime;/*如果heapWorkerCurrentObject非空则表示HeapWorker开始执行这个方法的时间*/
u8 heapWorkerInterpCpuStartTime; /*如果heapWorkerCurrentObject非空则表示HeapWorker CPU开始执行这个方法的时间*/
struct timespec heapWorkerNextTrim;/*下一次裁剪Heap Source的时间*/
GcMarkContext markContext;//做标记步骤中的状态
u8 gcStartTime;//GC开始的时间
bool gcRunning;//是否正在GC
enum{SR_COLLECT_NONE,SR_COLLECT_SOME,SR_COLLECT_ALL} softReferenceCollectionState;//GC时软引用对象回收多少,一点都不收集?回收一半?还是全部回收?
size_t softReferenceHeapSizeThreshold;/*存在多少软引用对象的时候开始回收软引用对象*/
int softReferenceColor;/*当软引用回收策略为回收一半时使用的概率值*/
bool markAllReferents;/*如果被设为true则任何软/弱/影子引用对象引用的对象都会被标记,如果设为false则采用普通的引用收集策略*/
下面都是统计调试跟踪用到的变量不去管它们
#if DVM_TRACK_HEAP_MARKING
size_t markCount;
size_t markSize;
#endif
int ddmHpifWhen;
int ddmHpsgWhen;
int ddmHpsgWhat;
int ddmNhsgWhen;
int ddmNhsgWhat;
#if WITH_HPROF
bool hprofDumpOnGc;
hprof_context_t *hprofContext;
#endif
};
Dalvik virtual machine的与内存管理相关的数据结构关系如图所示。为什么堆只有三个我想是因为初始化的时候生成的是一个堆,如果有Zygote进程则生成的时候又生成一个堆,再从Zygote进程fork新的进程的时候又生成一个堆,之后进程就永远对这个堆也就是被称为是当前堆进行操作,不会再生成新的堆了。
首先看看HeapWorker线程。它主要做执行对象的终结函数和引用对象的清理和归队工作,下面看它的代码。
static void doHeapWork(Thread *self)
{
Object *obj;
HeapWorkerOperation op;
int numFinalizersCalled, numReferencesEnqueued;
numFinalizersCalled = 0;
numReferencesEnqueued = 0;
while ((obj = dvmGetNextHeapWorkerObject(&op)) != NULL) {
Method *method = NULL;
/* Make sure the object hasn't been collected since
* being scheduled.
*/
/* Call the appropriate method(s).
*/
if (op == WORKER_FINALIZE) {
numFinalizersCalled++;
method = obj->clazz->vtable[gDvm.voffJavaLangObject_finalize];
callMethod(self, obj, method);
} else {
if (op & WORKER_ENQUEUE) {
numReferencesEnqueued++;
callMethod(self, obj,
gDvm.methJavaLangRefReference_enqueueInternal);
}
}
/* Let the GC collect the object.
*/
dvmReleaseTrackedAlloc(obj, self);
}
}
HeapWorker线程的主函数:
static void* heapWorkerThreadStart(void* arg)
{
Thread *self = dvmThreadSelf();
int cc;
/* tell the main thread that we're ready */
dvmLockMutex(&gDvm.heapWorkerLock);
gDvm.heapWorkerReady = true;
cc = pthread_cond_signal(&gDvm.heapWorkerCond);
dvmUnlockMutex(&gDvm.heapWorkerLock);
dvmLockMutex(&gDvm.heapWorkerLock);
while (!gDvm.haltHeapWorker) {
struct timespec trimtime;
bool timedwait = false;
/* We're done running interpreted code for now. */
dvmChangeStatus(NULL, THREAD_VMWAIT);
/* Signal anyone who wants to know when we're done. */
cc = pthread_cond_broadcast(&gDvm.heapWorkerIdleCond);
/* Trim the heap if we were asked to. */
trimtime = gDvm.gcHeap->heapWorkerNextTrim;
if (trimtime.tv_sec != 0 && trimtime.tv_nsec != 0) {
struct timeval now;
gettimeofday(&now, NULL);
if (trimtime.tv_sec < now.tv_sec ||
(trimtime.tv_sec == now.tv_sec &&
trimtime.tv_nsec <= now.tv_usec * 1000))
{
size_t madvisedSizes[HEAP_SOURCE_MAX_HEAP_COUNT];
/* The heap must be locked before the HeapWorker;
* unroll and re-order the locks. dvmLockHeap()
* will put us in VMWAIT if necessary. Once it
* returns, there shouldn't be any contention on
* heapWorkerLock.
*/
dvmUnlockMutex(&gDvm.heapWorkerLock);
dvmLockHeap();
dvmLockMutex(&gDvm.heapWorkerLock);
memset(madvisedSizes, 0, sizeof(madvisedSizes));
dvmHeapSourceTrim(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
dvmLogMadviseStats(madvisedSizes, HEAP_SOURCE_MAX_HEAP_COUNT);
dvmUnlockHeap();
trimtime.tv_sec = 0;
trimtime.tv_nsec = 0;
gDvm.gcHeap->heapWorkerNextTrim = trimtime;
} else {
timedwait = true;
}
}
/* sleep until signaled */
if (timedwait) {
cc = pthread_cond_timedwait(&gDvm.heapWorkerCond,
&gDvm.heapWorkerLock, &trimtime);
} else {
cc = pthread_cond_wait(&gDvm.heapWorkerCond, &gDvm.heapWorkerLock);
}
/* dvmChangeStatus() may block; don't hold heapWorkerLock.
*/
dvmUnlockMutex(&gDvm.heapWorkerLock);
dvmChangeStatus(NULL, THREAD_RUNNING);
dvmLockMutex(&gDvm.heapWorkerLock);
/* Process any events in the queue.
*/
doHeapWork(self);
}
dvmUnlockMutex(&gDvm.heapWorkerLock);
return NULL;
}
下面是显示GC收集的主函数。
void dvmCollectGarbageInternal(bool collectSoftReferences)
{
GcHeap *gcHeap = gDvm.gcHeap;
Object *softReferences;
Object *weakReferences;
Object *phantomReferences;
u8 now;
s8 timeSinceLastGc;
s8 gcElapsedTime;
int numFreed;
size_t sizeFreed;
/* The heap lock must be held.
*/
//先初始化一些变量
if (gcHeap->gcRunning) {
return;
}//如果GC已经在运行则退出
//......设置时间
dvmSuspendAllThreads(SUSPEND_FOR_GC);//挂起所有的线程
/* Get the priority (the "nice" value) of the current thread. The
* getpriority() call can legitimately return -1, so we have to
* explicitly test errno.
*/
//......获取当前线程的优先级
oldThreadPriority = priorityResult;
dvmLockMutex(&gDvm.heapWorkerLock);
/* Make sure that the HeapWorker thread hasn't become
* wedged inside interp code. If it has, this call will
* print a message and abort the VM.
*/
dvmAssertHeapWorkerThreadRunning();
这里插入dvmAssertHeapWorkerThreadRunning的代码看一下。
/* Make sure that the HeapWorker thread hasn't spent an inordinate
* amount of time inside interpreted a finalizer.
*
* Aborts the VM if the thread appears to be wedged.
*
* The caller must hold the heapWorkerLock to guarantee an atomic
* read of the watchdog values.
*/
void dvmAssertHeapWorkerThreadRunning()
{
if (gDvm.gcHeap->heapWorkerCurrentObject != NULL) {
static const u8 HEAP_WORKER_WATCHDOG_TIMEOUT = 10*1000*1000LL; // 10sec
u8 heapWorkerInterpStartTime = gDvm.gcHeap->heapWorkerInterpStartTime;
u8 now = dvmGetRelativeTimeUsec();
u8 delta = now - heapWorkerInterpStartTime;
u8 heapWorkerInterpCpuStartTime =
gDvm.gcHeap->heapWorkerInterpCpuStartTime;
u8 nowCpu = dvmGetOtherThreadCpuTimeUsec(gDvm.heapWorkerHandle);
u8 deltaCpu = nowCpu - heapWorkerInterpCpuStartTime;
if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT && gDvm.debuggerActive) {
/*
* Debugger suspension can block the thread indefinitely. For
* best results we should reset this explicitly whenever the
* HeapWorker thread is resumed. Ignoring the yelp isn't
* quite right but will do for a quick fix.
*/
LOGI("Debugger is attached -- suppressing HeapWorker watchdog/n");
heapWorkerInterpStartTime = now; /* reset timer */
} else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT) {
char* desc = dexProtoCopyMethodDescriptor(
&gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
LOGE("HeapWorker is wedged: %lldms spent inside %s.%s%s/n",
delta / 1000,
gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
free(desc);
dvmDumpAllThreads(true);
/* abort the VM */
dvmAbort();
} else if (delta > HEAP_WORKER_WATCHDOG_TIMEOUT / 2) {
char* desc = dexProtoCopyMethodDescriptor(
&gDvm.gcHeap->heapWorkerCurrentMethod->prototype);
LOGW("HeapWorker may be wedged: %lldms spent inside %s.%s%s/n",
delta / 1000,
gDvm.gcHeap->heapWorkerCurrentObject->clazz->descriptor,
gDvm.gcHeap->heapWorkerCurrentMethod->name, desc);
free(desc);
}
}
}
继续
/* Lock the pendingFinalizationRefs list.
*
* Acquire the lock after suspending so the finalizer
* thread can't block in the RUNNING state while
* we try to suspend.
*/
dvmLockMutex(&gDvm.heapWorkerListLock);
/* Set up the marking context.
*/
dvmHeapBeginMarkStep();
/* Mark the set of objects that are strongly reachable from the roots.
*/
dvmHeapMarkRootSet();
/* dvmHeapScanMarkedObjects() will build the lists of known
* instances of the Reference classes.
*/
gcHeap->softReferences = NULL;
gcHeap->weakReferences = NULL;
gcHeap->phantomReferences = NULL;
/* Make sure that we don't hard-mark the referents of Reference
* objects by default.
*/
gcHeap->markAllReferents = false;
/* Don't mark SoftReferences if our caller wants us to collect them.
* This has to be set before calling dvmHeapScanMarkedObjects().
*/
if (collectSoftReferences) {
gcHeap->softReferenceCollectionState = SR_COLLECT_ALL;
}
/* Recursively mark any objects that marked objects point to strongly.
* If we're not collecting soft references, soft-reachable
* objects will also be marked.
*/
dvmHeapScanMarkedObjects();
这里插入的代码看一下。
void dvmHeapScanMarkedObjects()
{
GcMarkContext *ctx = &gDvm.gcHeap->markContext;
/* The bitmaps currently have bits set for the root set.
* Walk across the bitmaps and scan each object.
*/
#ifndef NDEBUG
gLastFinger = 0;
#endif
dvmHeapBitmapWalkList(ctx->bitmaps, ctx->numBitmaps,
scanBitmapCallback, ctx);
/* We've walked the mark bitmaps. Scan anything that's
* left on the mark stack.
*/
processMarkStack(ctx);
}
这里插入processMarkStack的代码看一下。
static void processMarkStack(GcMarkContext *ctx)
{
const Object **const base = ctx->stack.base;
/* Scan anything that's on the mark stack.
* We can't use the bitmaps anymore, so use
* a finger that points past the end of them.
*/
ctx->finger = (void *)ULONG_MAX;
while (ctx->stack.top != base) {
scanObject(*ctx->stack.top++, ctx);
}
}
其中scanObject是一个复杂的函数,它调用了更为复杂的多个函数,太长太复杂了,在这里不便列出。其实最重要的一点是它之中还是会操作ctx所指向的栈,将新的对象加入栈顶,到下一次回到这个循环的时候,ctx->stack.top实际上又在栈顶了。如此下次实际上就代替深度优先搜索把所有的强可触及对象都给标记了。
继续
/* Latch these so that the other calls to dvmHeapScanMarkedObjects() don't
* mess with them.
*/
softReferences = gcHeap->softReferences;
weakReferences = gcHeap->weakReferences;
phantomReferences = gcHeap->phantomReferences;
/* All strongly-reachable objects have now been marked.
*/
if (gcHeap->softReferenceCollectionState != SR_COLLECT_NONE) {
dvmHeapHandleReferences(softReferences, REF_SOFT);
// markCount always zero
/* Now that we've tried collecting SoftReferences,
* fall back to not collecting them. If the heap
* grows, we will start collecting again.
*/
gcHeap->softReferenceCollectionState = SR_COLLECT_NONE;
} // else dvmHeapScanMarkedObjects() already marked the soft-reachable set
dvmHeapHandleReferences(weakReferences, REF_WEAK);
// markCount always zero
/* Once all weak-reachable objects have been taken
* care of, any remaining unmarked objects can be finalized.
*/
dvmHeapScheduleFinalizations();
/* Any remaining objects that are not pending finalization
* could be phantom-reachable. This will mark any phantom-reachable
* objects, as well as enqueue their references.
*/
dvmHeapHandleReferences(phantomReferences, REF_PHANTOM);
在以上这段代码中其实又多次调用了processMarkStack方法,把引用对象相关的强可触及对象又都标记上了。
dvmHeapSweepUnmarkedObjects(&numFreed, &sizeFreed);
dvmHeapFinishMarkStep();
/* Now's a good time to adjust the heap size, since
* we know what our utilization is.
*
* This doesn't actually resize any memory;
* it just lets the heap grow more when necessary.
*/
dvmHeapSourceGrowForUtilization();
dvmHeapSizeChanged();
/* Now that we've freed up the GC heap, return any large
* free chunks back to the system. They'll get paged back
* in the next time they're used. Don't do it immediately,
* though; if the process is still allocating a bunch of
* memory, we'll be taking a ton of page faults that we don't
* necessarily need to.
*
* Cancel any old scheduled trims, and schedule a new one.
*/
dvmScheduleHeapSourceTrim(5); // in seconds
gcHeap->gcRunning = false;
dvmUnlockMutex(&gDvm.heapWorkerListLock);
dvmUnlockMutex(&gDvm.heapWorkerLock);
dvmResumeAllThreads(SUSPEND_FOR_GC);
//......其它一些事情
}
dvmHeapSweepUnmarkedObjects又是一个巨复杂的函数,其中会调用到的释放堆中的对象的函数是dvmHeapSourceFree,可以看看这个函数。
void dvmHeapSourceFree(void *ptr)
{
Heap *heap;
HS_BOILERPLATE();
heap = ptr2heap(gHs, ptr);
if (heap != NULL) {
countFree(heap, ptr, true);
/* Only free objects that are in the active heap.
* Touching old heaps would pull pages into this process.
*/
if (heap == gHs->heaps) {
mspace_free(heap->msp, ptr);
}
}
}
其中的mspace_free函数就是真正的堆空间的释放和压缩,但是这一已经是dlmalloc的部分不是属于虚拟机的内存管理了。
Dalvik virtual Machine的初始化代码里有GC的初始化。
int dvmStartup(int argc, const char* const argv[], bool ignoreUnrecognized,
JNIEnv* pEnv)
{
//......
if (!dvmGcStartup())
goto fail;
//......
}
bool dvmGcStartup(void)
{
dvmInitMutex(&gDvm.gcHeapLock);
return dvmHeapStartup();
}
bool dvmHeapStartup()
{
GcHeap *gcHeap;
#if defined(WITH_ALLOC_LIMITS)
gDvm.checkAllocLimits = false;
gDvm.allocationLimit = -1;
#endif
gcHeap = dvmHeapSourceStartup(gDvm.heapSizeStart, gDvm.heapSizeMax);
if (gcHeap == NULL) {
return false;
}
gcHeap->heapWorkerCurrentObject = NULL;
gcHeap->heapWorkerCurrentMethod = NULL;
gcHeap->heapWorkerInterpStartTime = 0LL;
gcHeap->softReferenceCollectionState = SR_COLLECT_NONE;
gcHeap->softReferenceHeapSizeThreshold = gDvm.heapSizeStart;
gcHeap->ddmHpifWhen = 0;
gcHeap->ddmHpsgWhen = 0;
gcHeap->ddmHpsgWhat = 0;
gcHeap->ddmNhsgWhen = 0;
gcHeap->ddmNhsgWhat = 0;
#if WITH_HPROF
gcHeap->hprofDumpOnGc = false;
gcHeap->hprofContext = NULL;
#endif
/* This needs to be set before we call dvmHeapInitHeapRefTable().
*/
gDvm.gcHeap = gcHeap;
/* Set up the table we'll use for ALLOC_NO_GC.
*/
if (!dvmHeapInitHeapRefTable(&gcHeap->nonCollectableRefs,
kNonCollectableRefDefault))
{
LOGE_HEAP("Can't allocate GC_NO_ALLOC table/n");
goto fail;
}
/* Set up the lists and lock we'll use for finalizable
* and reference objects.
*/
dvmInitMutex(&gDvm.heapWorkerListLock);
gcHeap->finalizableRefs = NULL;
gcHeap->pendingFinalizationRefs = NULL;
gcHeap->referenceOperations = NULL;
/* Initialize the HeapWorker locks and other state
* that the GC uses.
*/
dvmInitializeHeapWorkerState();
return true;
fail:
gDvm.gcHeap = NULL;
dvmHeapSourceShutdown(gcHeap);
return false;
}
GcHeap *
dvmHeapSourceStartup(size_t startSize, size_t absoluteMaxSize)
{
GcHeap *gcHeap;
HeapSource *hs;
Heap *heap;
mspace msp;
assert(gHs == NULL);
if (startSize > absoluteMaxSize) {
LOGE("Bad heap parameters (start=%d, max=%d)/n",
startSize, absoluteMaxSize);
return NULL;
}
/* Create an unlocked dlmalloc mspace to use as
* the small object heap source.
*/
msp = createMspace(startSize, absoluteMaxSize, 0);
if (msp == NULL) {
return false;
}
/* Allocate a descriptor from the heap we just created.
*/
gcHeap = mspace_malloc(msp, sizeof(*gcHeap));
if (gcHeap == NULL) {
LOGE_HEAP("Can't allocate heap descriptor/n");
goto fail;
}
memset(gcHeap, 0, sizeof(*gcHeap));
hs = mspace_malloc(msp, sizeof(*hs));
if (hs == NULL) {
LOGE_HEAP("Can't allocate heap source/n");
goto fail;
}
memset(hs, 0, sizeof(*hs));
hs->targetUtilization = DEFAULT_HEAP_UTILIZATION;
hs->minimumSize = 0;
hs->startSize = startSize;
hs->absoluteMaxSize = absoluteMaxSize;
hs->idealSize = startSize;
hs->softLimit = INT_MAX; // no soft limit at first
hs->numHeaps = 0;
hs->sawZygote = gDvm.zygote;
if (!addNewHeap(hs, msp, absoluteMaxSize)) {
LOGE_HEAP("Can't add initial heap/n");
goto fail;
}
gcHeap->heapSource = hs;
countAllocation(hs2heap(hs), gcHeap, false);
countAllocation(hs2heap(hs), hs, false);
gHs = hs;
return gcHeap;
fail:
destroy_contiguous_mspace(msp);
return NULL;
}
完成分配以后,内存结构如图所示。
然后看关闭的过程
void dvmGcShutdown(void)
{
//TODO: grab and destroy the lock
dvmHeapShutdown();
}
void dvmHeapShutdown()
{
//TODO: make sure we're locked
if (gDvm.gcHeap != NULL) {
GcHeap *gcHeap;
gcHeap = gDvm.gcHeap;
gDvm.gcHeap = NULL;
/* Tables are allocated on the native heap;
* they need to be cleaned up explicitly.
* The process may stick around, so we don't
* want to leak any native memory.
*/
dvmHeapFreeHeapRefTable(&gcHeap->nonCollectableRefs);
dvmHeapFreeLargeTable(gcHeap->finalizableRefs);
gcHeap->finalizableRefs = NULL;
dvmHeapFreeLargeTable(gcHeap->pendingFinalizationRefs);
gcHeap->pendingFinalizationRefs = NULL;
dvmHeapFreeLargeTable(gcHeap->referenceOperations);
gcHeap->referenceOperations = NULL;
/* Destroy the heap. Any outstanding pointers
* will point to unmapped memory (unless/until
* someone else maps it). This frees gcHeap
* as a side-effect.
*/
dvmHeapSourceShutdown(gcHeap);
}
}
void dvmHeapSourceShutdown(GcHeap *gcHeap)
{
if (gcHeap != NULL && gcHeap->heapSource != NULL) {
HeapSource *hs;
size_t numHeaps;
size_t i;
hs = gcHeap->heapSource;
gHs = NULL;
/* Cache numHeaps because hs will be invalid after the last
* heap is freed.
*/
numHeaps = hs->numHeaps;
for (i = 0; i < numHeaps; i++) {
Heap *heap = &hs->heaps[i];
dvmHeapBitmapDelete(&heap->objectBitmap);
destroy_contiguous_mspace(heap->msp);
}
/* The last heap is the original one, which contains the
* HeapSource object itself.
*/
}
}
发表评论
-
Java中循环嵌套跳出的高效写法
2014-04-24 19:31 3173(下面写的这些并不是Jdk的新特性,也不是Java某个版本提供 ... -
Android各版本新增功能一览(转)
2013-12-05 20:43 1697Android3.0新增功能:主要增加全息主题 ... -
NotificationManager和Notification的使用总结(转)
2013-11-26 12:51 625这几天一直在修改twig ... -
Android应用程序资源的编译和打包过程分析 (转自老罗的博客)
2013-11-13 10:57 15568我们知道,在一个APK文件中,除了有代码文件之外,还 ... -
Proguard 源码分析 (七) 混淆
2013-11-10 21:19 1804本章我们讲Proguard非常重要的一个步骤:混淆Obfu ... -
Proguard源码分析(六)前文总结
2013-11-05 14:40 1078目前,我们读了Proguard的 ... -
Proguard源码分析(五) ConfigurationParser.keep参数
2013-10-31 14:43 3929本章节我们绕回来讲Keep参数,也就是Configurat ... -
Proguard源码分析(四) 压缩
2013-10-30 10:59 1126上一次我们讲了seed文件,这次我们说压缩,对应的输出文件是 ... -
Proguard源码分析(三)Seed文件
2013-10-29 12:45 1527Seed文件就是保持住的类文件,直白一点就是不被混淆的文件, ... -
Proguard源码分析(二)输出文件
2013-10-28 10:28 915ProGuard outputs thefollowing ... -
Android 2.2.2到Android 4.2.2源码下载地址(转)
2013-10-28 10:13 1006Android 2.2.2到Android 4.2.2源码下 ... -
代码混淆器Proguard源码分析(一) 读取
2013-10-22 19:40 2310Proguard是Android中经常用的混淆工具,当然你也 ... -
Android内存之VSS/RSS/PSS/USS
2013-09-25 14:07 691Terms VSS - Vi ... -
chrome开源工程(转)
2013-09-13 10:42 1314在chrome地址栏输入about:credits就可以看 ... -
dex文件结构(转)
2013-09-03 14:10 1031Dex文件和Dalvik虚拟机 在Android系统中 ... -
android clipPath切割画布
2013-08-29 12:00 6274(转自:http://wallage.blog.163.co ... -
Android WebView控件
2013-06-06 11:46 2249android.webkit库聚合了webkit内核的浏览器 ... -
dumpsys命令~(非常有用~)
2013-04-03 12:02 0dumpsys是系统中重要的函数,我们来看看它如何使用,都能 ... -
内存监控命令~
2013-04-03 00:30 0meminfo 命令:cat /proc/meminfo ... -
adb常用命令
2013-04-03 00:17 1097Android 调试桥(adb)是多种用途的工具,该工具可以 ...
相关推荐
本篇文章将深入探讨Dalvik VM的字节码及其指令格式,基于Android 2.3.7源码进行解析。** 首先,我们需要理解Dalvik字节码的概念。Dalvik字节码是一种专门为Android设计的中间代码,与Java虚拟机(JVM)中的Java字节...
《Android虚拟机内部原理剖析——Dalvik VM详解》 Android操作系统以其开源、灵活的特性深受开发者喜爱,而作为Android系统核心组成部分的Dalvik虚拟机,是Android应用运行的基础。了解和掌握Dalvik虚拟机的内部...
在Android操作系统中,Dalvik VM是一种专为移动设备优化的虚拟机,它执行基于 Dex(Dalvik Executable)格式的字节码。下面将详细阐述Dalvik VM的字节码及其相关知识点。 1. **Dalvik VM结构**: - Dalvik VM是...
达尔文虚拟机(Dalvik Virtual Machine,简称Dalvik VM)是为Android平台设计的一种虚拟机,它运行的是.dex(Dalvik Executable)格式的字节码文件,这种格式的字节码被设计为高度优化的、适合于移动设备执行的代码...
《Android Dalvik VM 内部机制详解》 在Android操作系统中,Dalvik虚拟机(Dalvik Virtual Machine,简称Dalvik VM)是其核心组件之一,它负责运行Android应用程序。Dalvik VM的设计目标是确保类型安全和引用安全,...
Dalvik VM(Dalvik虚拟机)是Android平台中用于运行应用程序的核心组件之一。与传统的Java虚拟机不同,Dalvik VM专为移动设备设计,针对内存和处理器性能进行了优化。它通过将应用程序打包成DEX(Dalvik Executable...
但我可以根据标题所揭示的主题,即“Android Dalvik虚拟机结构及机制剖析”,来生成相关的知识点。 Dalvik是Android操作系统中用于执行应用程序的虚拟机。它专为移动设备优化,使得Android应用能够高效运行。Dalvik...
进入 Android Dalvik 虚拟机,android dalvik介绍 Dalvik 虚拟机的特点——掌握 Android 程序的运行原理 Android 系统的架构采用分层思想,这样的好处是拥有减少各层之间的依赖性、便于独 立分发、容易收敛问题和...
Android Dalvik 文件编译方法 Android Dalvik 文件编译方法是 Android 应用程序开发过程中的一步关键步骤。 Dalvik 是 Android 操作系统中的虚拟机字节码格式,所有 Android 应用程序都需要将 Java 代码编译成 ...
Android Dalvik源码是Android操作系统核心组件之一,它是Android虚拟机(VM)的早期实现,主要负责运行基于Dalvik字节码的应用程序。在深入理解Android Dalvik源码之前,我们首先需要了解一些基本概念。 1. **...
标题 "Google Android Dalvik JavaVM Kernel Code MIPS Version" 指的是 Google 在 Android 系统中针对 MIPS 架构实现的 Dalvik 虚拟机内核代码。Dalvik 是 Android 系统早期采用的一种虚拟机,它专门设计用于运行 ...
Dalvik VM是Android系统的核心组成部分,它是基于Just-In-Time (JIT) 编译的,负责解析和执行Android应用的.dex文件中的字节码。.dex文件是Android应用程序的可执行格式,包含了类定义、方法和数据。Dexmaker则提供...
Android Dalvik虚拟机是Android操作系统的核心组件之一,它为Android应用程序提供了一个运行环境。本卷主要探讨了Dalvik虚拟机的结构与工作机制,旨在深入理解Android应用的执行流程和优化策略。 首先,我们要理解...
**Android Dalvik虚拟机讲义** Android Dalvik虚拟机是Android操作系统的核心组成部分,它是Google为移动设备特别设计的一种高效、轻量级的虚拟机。在Android系统中,应用程序以Dalvik可执行文件(.dex)的形式运行...
三、Dalvik VM结构 1. 类加载器:负责加载应用的类文件,根据类名找到对应的.dex文件,并将其加载到内存中。 2. 方法区:存储类的元数据,包括类名、方法签名、常量池等。 3. 堆:分配对象实例的内存,支持垃圾回收...
《Android Dalvik虚拟机结构及机制剖析》《Android Dalvik虚拟机结构及机制剖析》《Android Dalvik虚拟机结构及机制剖析》《Android Dalvik虚拟机结构及机制剖析》《Android Dalvik虚拟机结构及机制剖析》《Android ...
根据提供的文件信息,我们可以了解到文件内容是关于Android Dalvik虚拟机的结构及机制的深入剖析。虽然文件本身并没有提供实际的内容,但我们可以从标题和描述中提取出相关知识点。 首先,标题“Android Dalvik...
【Dalvik VM】 Dalvik虚拟机是Android操作系统的核心组件之一,它负责执行应用程序的字节码,使得Android应用能够在设备上运行。Dalvik的设计目标是适应资源有限的移动设备,如低性能CPU、少量RAM以及慢速内部闪存。...
Android Dalvik虚拟机是Android操作系统的核心组成部分,它为Android应用程序提供了运行环境。在这个环境中,应用程序的代码被转换成Dalvik可执行格式,称为.dex(Dalvik Executable)文件,然后在此虚拟机上运行。...