Dalvik GC

作者: dexteryu | 来源:发表于2017-07-27 16:07 被阅读0次
    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();
    }
    
    相关资料

    相关文章

      网友评论

          本文标题:Dalvik GC

          本文链接:https://www.haomeiwen.com/subject/howthxtx.html