Android Dalvik虚拟机 对象创建内存分配流程

前言

本篇文章介绍我们在日常开发使用Java时new对象的时,Dalvik在堆上的内存分配是如何分配的。内存又和gc相关,下篇文章会分析Dalvik的gc流程。

Dalvik内存管理

内存碎片问题其实是一个通用的问题,不单止Dalvik虚拟机在Java堆为对象分配内存时会遇到,C库的malloc函数在分配内存时也会遇到。Android系统使用的C库bionic使用了Doug Lea写的dlmalloc内存分配器。也就是说,我们调用函数malloc的时候,使用的是dlmalloc内存分配器来分配内存。
Dalvik虚拟机的Java堆的底层实现是一块匿名共享内存,并且将其抽象为C库的一个mspace,即Dalvik虚拟机是利用的C库里面的dlmalloc内存分配器来解决内存碎片问题。
从Dailvk代码设计上存在两种内存限制,这两种限制在随着内存分配堆增长的过程中在不断调整,本篇文章分析内存分配流程,有机会分析内存管理这块,即Dalvik是如何调整堆的算法的,也比较复杂:

  1. 一是Dalvik设计的softLimit,即在Active堆中可以分配的内存上限;
  2. 二是mspace的hardLimit限制,即是匿名共享内存里的mspace_footprint限制;

对象创建内存分配流程

  1. Java中进行对象分配最终会通过tryMalloc进行内存分配
  2. tryMalloc操作的核心方法是dvmHeapSourceAlloc和dvmHeapSourceAllocAndGrow
  3. tryMalloc中,首次调用dvmHeapSourceAlloc进行分配,此时如果分配成功,超过concurrentStartBytes时则会触发concurrent gc,此为并发gc,较为轻量级
  4. dvmHeapSourceAlloc分配失败的原因可以是softlimit或mspace的footprint达到阈值
  5. 在首次执行dvmHeapSourceAlloc失败后触发GC_FOR_MALLOC进行同步内存回收,是stop world类型gc,最为耗时
  6. 如果此时还失败的话,会尝试调用dvmHeapSourceAllocAndGrow,先remove掉softLimt尝试分配,不行的话再把footprint增到最大再分配,分配完成后会将footprint还原
  7. 如果调用dvmHeapSourceAllocAndGrow仍然失败,那么会触发GC_BEFORE_OOM进行回收,这里传入参数true表示也要讲软引用一块清除掉了
  8. 软引用清除掉后之后再会尝试dvmHeapSourceAllocAndGrow,如果还是失败,那么会抛出OutOfMemoryError错误
    在这里插入图片描述

Heap.cpp

dvmMalloc

Java的动态内存分配是通过new操作符完成的,而最终调用的是dvmMalloc()函数,Dalvik在内存分配上也在尽力分配出内存,直到最后OOM。

// Allocate storage on the GC heap.  We guarantee 8-byte alignment.
void* dvmMalloc(size_t size, int flags)
{
    
    
    void *ptr;

    dvmLockHeap();
    // Try as hard as possible to allocate some memory.
    ptr = tryMalloc(size);
    dvmUnlockHeap();

    if (ptr != NULL) {
    
    
        // track it,此处在下篇文章分析,这里可以dump出来分配的内存和堆栈
        if ((flags & ALLOC_DONT_TRACK) == 0) {
    
    
            dvmAddTrackedAlloc((Object*)ptr, NULL);
        }
    } else {
    
    
        // The allocation failed; throw an OutOfMemoryError.
        throwOOME();
    }

    return ptr;
}

tryMalloc

// Try as hard as possible to allocate some memory.
static void *tryMalloc(size_t size)
{
    
    
    void *ptr;
    ptr = dvmHeapSourceAlloc(size);
    if (ptr != NULL) {
    
    
        return ptr;
    }

    // The allocation failed.  If the GC is running, block until it completes and retry.
    if (gDvm.gcHeap->gcRunning) {
    
    
        // The GC is concurrently tracing the heap.  Release the heap lock, wait for the GC to complete, and retrying allocating.
        dvmWaitForConcurrentGcToComplete();
    } else {
    
    
      // Try a foreground GC since a concurrent GC is not currently running.
      gcForMalloc(false); 
    }

    ptr = dvmHeapSourceAlloc(size);
    if (ptr != NULL) {
    
    
        return ptr;
    }

    // Even that didn't work;  this is an exceptional state. Try harder, growing the heap if necessary.
    ptr = dvmHeapSourceAllocAndGrow(size);
    if (ptr != NULL) {
    
    
    	// may want to grow a little bit more so that the amount of free space is equal to the old free space + the utilization slop for the new allocation.
        size_t newHeapSize;
        newHeapSize = dvmHeapSourceGetIdealFootprint();
        return ptr;
    }

    /* Most allocations should have succeeded by now, so the heap
     * is really full, really fragmented, or the requested size is
     * really big.  Do another GC, collecting SoftReferences this
     * time.  The VM spec requires that all SoftReferences have
     * been collected and cleared before throwing an OOME.
     */
    gcForMalloc(true);
    ptr = dvmHeapSourceAllocAndGrow(size);
    if (ptr != NULL) {
    
    
        return ptr;
    }
    dvmDumpThread(dvmThreadSelf(), false);
    return NULL;
}

HeapSouce.cpp

dvmHeapSourceAlloc

// Allocates <n> bytes of zeroed data.
void* dvmHeapSourceAlloc(size_t n) {
    
    
    HeapSource *hs = gHs;
    Heap* heap = hs2heap(hs);

    if (heap->bytesAllocated + n > hs->softLimit) {
    
    
        // softLimit限制;This allocation would push us over the soft limit; act as if the heap is full.
        return NULL;
    }
    void* ptr = mspace_calloc(heap->msp, 1, n);
    if (ptr == NULL) {
    
    
    	// hardLimit限制
    	return NULL;
    }

    countAllocation(heap, ptr);
    // Check to see if a concurrent GC should be initiated. The garbage collector thread is already running or has yet to be started.  Do nothing.
    if (gDvm.gcHeap->gcRunning || !hs->hasGcThread) {
    
    
        return ptr;
    }
    // 唤醒concurrent gc. We have exceeded the allocation threshold.  Wake up the garbage collector.
    if (heap->bytesAllocated > heap->concurrentStartBytes) {
    
    
        dvmSignalCond(&gHs->gcThreadCond);
    }
    return ptr;
}

dvmHeapSourceAllocAndGrow

// Allocates <n> bytes of zeroed data, growing as much as possible if necessary.
void* dvmHeapSourceAllocAndGrow(size_t n) {
    
    
    HeapSource *hs = gHs;
    Heap* heap = hs2heap(hs);
    void* ptr = dvmHeapSourceAlloc(n);
    if (ptr != NULL) {
    
    
        return ptr;
    }

    size_t oldIdealSize = hs->idealSize;
    // We're soft-limited.  Try removing the soft limit to see if we can allocate without actually growing.
    if (isSoftLimited(hs)) {
    
    
        hs->softLimit = SIZE_MAX;
        ptr = dvmHeapSourceAlloc(n);
        if (ptr != NULL) {
    
    
            snapIdealFootprint();
            return ptr;
        }
    }

    // We're not soft-limited.  Grow the heap to satisfy the request. If this call fails, no footprints will have changed.
    ptr = heapAllocAndGrow(hs, heap, n);
    if (ptr != NULL) {
    
    
        snapIdealFootprint();
    } else {
    
    
        setIdealFootprint(oldIdealSize);
    }
    return ptr;
}


// Remove any hard limits, try to allocate, and shrink back down. Last resort when trying to allocate an object.
static void* heapAllocAndGrow(HeapSource *hs, Heap *heap, size_t n)
{
    
    
    // Grow as much as possible, but don't let the real footprint go over the absolute max.
    size_t max = heap->maximumSize;
    mspace_set_footprint_limit(heap->msp, max);
    
    void* ptr = dvmHeapSourceAlloc(n);

    // Shrink back down as small as possible.  Our caller may readjust max_allowed to a more appropriate value.
    mspace_set_footprint_limit(heap->msp, mspace_footprint(heap->msp));
    
    return ptr;
}

Dalvik虚拟机为新创建对象分配内存的过程分析

猜你喜欢

转载自blog.csdn.net/u014099894/article/details/128995279