Dalvik GC

COW策略

一开始的时候(未复制Zygote进程的地址空间的时候),应用程序进程和Zygote进程共享了同一个用来分配对象的堆。当Zygote进程或者应用程序进程对该堆进行写操作时,内核就会执行真正的拷贝操作,使得Zygote进程和应用程序进程分别拥有自己的一份拷贝,这就是所谓的COW。因为copy是十分耗时的,所以必须尽量避免copy或者尽量少的copy。

和GC有关的一些指标

在启动Dalvik虚拟机的时候,我们可以分别通过-Xms、-Xmx和-XX:HeapGrowthLimit三个选项来指定上述三个值,以上三个值分别表示表示:

  • Starting Size: Dalvik虚拟机启动的时候,会先分配一块初始的堆内存给虚拟机使用。
  • Growth Limit:是系统给每一个程序的最大堆上限,超过这个上限,程序就会OOM
  • Maximum Size:不受控情况下的最大堆内存大小,起始就是我们在用largeheap属性的时候,可以从系统获取的最大堆大小

同时除了上面的这个三个指标外,还有几个指标也是值得我们关注的,那就是堆最小空闲值(Min Free)、堆最大空闲值(Max Free)和堆目标利用率(Target Utilization)。假设在某一次GC之后,存活对象占用内存的大小为LiveSize,那么这时候堆的理想大小应该为(LiveSize / U)。但是(LiveSize / U)必须大于等于(LiveSize + MinFree)并且小于等于(LiveSize + MaxFree),每次GC后垃圾回收器都会尽量让堆的利用率往目标利用率靠拢。所以当我们尝试手动去生成一些几百K的对象,试图去扩大可用堆大小的时候,反而会导致频繁的GC,因为这些对象的分配会导致GC,而GC后会让堆内存回到合适的比例,而我们使用的局部变量很快会被回收理论上存活对象还是那么多,我们的堆大小也会缩减回来无法达到扩充的目的。 与此同时这也是产生CONCURRENT GC的一个因素。

GC的类型
  • GC_FOR_MALLOC: 表示是在堆上分配对象时内存不足触发的GC。
  • GC_CONCURRENT: 当我们应用程序的堆内存达到一定量,或者可以理解为快要满的时候,系统会自动触发GC操作来释放内存。
  • GC_EXPLICIT: 表示是应用程序调用System.gc、VMRuntime.gc接口或者收到SIGUSR1信号时触发的GC。
  • GC_BEFORE_OOM: 表示是在准备抛OOM异常之前进行的最后努力而触发的GC。
GC类型 isPartial isConcurrent doPreserve
GC_FOR_MALLOC Active 非并行 不回收软引
GC_CONCURRENT Active 并行 不回收软引
GC_EXPLICIT Active&Zygote 并行 不回收软引
GC_BEFORE_OOM Active&Zygote 非并行 回收软引
对象的分配和GC触发时机

在对象的分配中会导致GC,第一次分配对象失败我们会触发GC但是不回收Soft的引用,如果再次分配还是失败我们就会将Soft的内存也给回收,前者触发的GC是GC_FOR_MALLOC类型的GC,后者是GC_BEFORE_OOM类型的GC。而当内存分配成功后,我们会判断当前的内存占用是否是达到了GC_CONCURRENT的阀值,如果达到了那么又会触发GC_CONCURRENT。

Java堆创建过程分析
  • Alloc.cpp: dvmGcStartup
bool dvmGcStartup()
{
    dvmInitMutex(&gDvm.gcHeapLock);
    pthread_cond_init(&gDvm.gcHeapCond, NULL);
    return dvmHeapStartup();
}
  • Heap.cpp: dvmHeapStartup
bool dvmHeapStartup()
{
    GcHeap *gcHeap;
    gcHeap = dvmHeapSourceStartup(gDvm.heapStartingSize,
                                  gDvm.heapMaximumSize,
                                  gDvm.heapGrowthLimit);
    if (gcHeap == NULL) {
        return false;
    }
    gDvm.gcHeap = gcHeap;
    if (!dvmCardTableStartup(gDvm.heapMaximumSize, gDvm.heapGrowthLimit)) {
        return false;
    }
    return true;
}
创建对象分配内存的过程
  • Alloc.cpp: dvmAllocObject
Object* dvmAllocObject(ClassObject* clazz, int flags)
{
    Object* newObj;
    newObj = (Object*)dvmMalloc(clazz->objectSize, flags);
    if (newObj != NULL) {
        DVM_OBJECT_INIT(newObj, clazz);
        dvmTrackAllocation(clazz, clazz->objectSize);   // notify DDMS 
    }
    return newObj;
}
  • Heap.cpp: dvmMalloc
void* dvmMalloc(size_t size, int flags)
{
    dvmLockHeap();
    ptr = tryMalloc(size);
    dvmUnlockHeap();
    if (ptr == NULL)  throwOOME();
    return ptr;
}
  • Heap.cpp: tryMalloc
static void *tryMalloc(size_t size)
{
    ptr = dvmHeapSourceAlloc(size);
    if (ptr != NULL)   return ptr;
    if (gDvm.gcHeap->gcRunning) {
        dvmWaitForConcurrentGcToComplete();
    } else {
      gcForMalloc(false);
    }
    ptr = dvmHeapSourceAlloc(size);
    if (ptr != NULL)  return ptr;
    ptr = dvmHeapSourceAllocAndGrow(size);
    if (ptr != NULL)  return ptr;
    gcForMalloc(true);
    ptr = dvmHeapSourceAllocAndGrow(size);
    if (ptr != NULL)  return ptr;
    LOGE_HEAP("Out of memory on a %zd-byte allocation.", size);
    dvmDumpThread(dvmThreadSelf(), false); 
    return NULL;
}
GC过程
  • GC_BEFORE_OOM 、GC_FOR_MALLOC
// Heap.cpp: gcForMalloc
static void gcForMalloc(bool clearSoftReferences)
{
    const GcSpec *spec = clearSoftReferences ? GC_BEFORE_OOM : GC_FOR_MALLOC;
    dvmCollectGarbageInternal(spec);
}
// Heap.cpp: dvmCollectGarbageInternal
void dvmCollectGarbageInternal(const GcSpec* spec)
{
    gcHeap->gcRunning = true;
    dvmSuspendAllThreads(SUSPEND_FOR_GC);
    // 初始化Mark Stack,并且设定好GC范围
    if (!dvmHeapBeginMarkStep(spec->isPartial)) {
        dvmAbort();
    }
    dvmHeapMarkRootSet();
    if (spec->isConcurrent) {
        dvmResumeAllThreads(SUSPEND_FOR_GC);
    }
    dvmHeapScanMarkedObjects();
    if (spec->isConcurrent) {
        dvmSuspendAllThreads(SUSPEND_FOR_GC);
        dvmHeapReMarkRootSet();
        dvmHeapReScanMarkedObjects();
    }
    if (spec->isConcurrent) {
        dvmResumeAllThreads(SUSPEND_FOR_GC);
    }
   // 根据设置的堆目标利用率调整堆的大小
    dvmHeapSourceGrowForUtilization();
    gcHeap->gcRunning = false;
    if (!spec->isConcurrent) {
        dvmResumeAllThreads(SUSPEND_FOR_GC);
    }
}
  • GC_CONCURRENT
// HeapSource.cpp: dvmHeapSourceAlloc
void* dvmHeapSourceAlloc(size_t n)
{
    void* ptr = mspace_calloc(heap->msp, 1, n);
    if (ptr == NULL) {
        return NULL;
    }
    countAllocation(heap, ptr);
    if (gDvm.gcHeap->gcRunning || !hs->hasGcThread) {
        return ptr;
    }
    if (heap->bytesAllocated > heap->concurrentStartBytes) {
       // 超过阈值,触发GC --> gcDaemonThread
        dvmSignalCond(&gHs->gcThreadCond);
    }
    return ptr;
}
static void *gcDaemonThread(void* arg)
{
    while (gHs->gcThreadShutdown != true) {
        bool trim = false;
        if (gHs->gcThreadTrimNeeded) {
            int result = dvmRelativeCondWait(&gHs->gcThreadCond, &gHs->gcThreadMutex,
                    HEAP_TRIM_IDLE_TIME_MS, 0);
            if (result == ETIMEDOUT) {
                /* Timed out waiting for a GC request, schedule a heap trim. */
                trim = true;
            }
        } else {
            dvmWaitCond(&gHs->gcThreadCond, &gHs->gcThreadMutex);
        }
        if (!gDvm.gcHeap->gcRunning) {
            dvmChangeStatus(NULL, THREAD_RUNNING);
            if (trim) {
                trimHeaps();
                gHs->gcThreadTrimNeeded = false;
            } else {
                dvmCollectGarbageInternal(GC_CONCURRENT);
                gHs->gcThreadTrimNeeded = true;
            }
        }
    }
    return NULL;
}

GC线程平时没事的时候,就在条件变量gHs->gcThreadCond上进行等待HEAP_TRIM_IDLE_TIME_MS毫秒(5000毫秒)。如果在HEAP_TRIM_IDLE_TIME_MS毫秒内,都没有得到执行GC的通知,那么它就调用函数trimHeaps对Java堆进行裁剪,以便可以将堆上的一些没有使用到的内存交还给内核。

  • GC_EXPLICIT
// Alloc.app: dvmCollectGarbage
void dvmCollectGarbage()
{
    if (gDvm.disableExplicitGc) {
        return;
    }
    dvmLockHeap();
    dvmWaitForConcurrentGcToComplete();
    dvmCollectGarbageInternal(GC_EXPLICIT);
    dvmUnlockHeap();
}
相关资料
  • Android GC 原理探究
  • 老罗讲Dalvik虚拟机

你可能感兴趣的:(Dalvik GC)