<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                [原文出處-------------Dalvik虛擬機為新創建對象分配內存的過程分析](http://blog.csdn.net/luoshengyang/article/details/41688319) 在前面一文中,我們分析了Dalvik虛擬機創建Java堆的過程。有了Java堆之后,Dalvik虛擬機就可以在上面為對象分配內存了。在Java堆為對象分配內存需要解決內存碎片和內存不足兩個問題。要解決內存碎片問題,就要找到一塊大小最合適的空閑內存分配給對象使用。而內存不足有可能是內存配額用完引起的,也有可能是垃圾沒有及時回收引起的,要區別對待。本文就詳細分析Dalvik虛擬機是如何解決這些問題的。 內存碎片問題其實是一個通用的問題,不單止Dalvik虛擬機在Java堆為對象分配內存時會遇到,C庫的malloc函數在分配內存時也會遇到。Android系統使用的C庫bionic使用了[Doug Lea](http://gee.cs.oswego.edu/)寫的dlmalloc內存分配器。也就是說,我們調用函數malloc的時候,使用的是dlmalloc內存分配器來分配內存。這是一個成熟的內存分配器,可以很好地解決內存碎片問題。關于dlmalloc內存分配器的設計,可以參考這篇文章:[A Memory Allocator](http://gee.cs.oswego.edu/dl/html/malloc.html)。 前面[Dalvik虛擬機垃圾收集機制簡要介紹和學習計劃](http://blog.csdn.net/luoshengyang/article/details/41338251)一文提到,Dalvik虛擬機的Java堆的底層實現是一塊匿名共享內存,并且將其抽象為C庫的一個mspace,如圖1所示: ![](https://box.kancloud.cn/e64d904089a3a31aa4fbd63694ad0691_305x167.png) 圖1 Dalvik虛擬機Java堆 于是,Dalvik虛擬機就很機智地利用C庫里面的dlmalloc內存分配器來解決內存碎片問題! 為了應對可能面臨的內存不足問題,Dalvik虛擬機采用一種漸進的方法來為對象分配內存,直到盡了最大努力,如圖2所示: ![](https://box.kancloud.cn/26b5911414d985ee79a87e5b464d5e85_804x834.png) 圖2 Dalvik虛擬機為對象分配內存的過程 接下來,我們就詳細分析這個過程,以便可以了解Dalvik虛擬機是如何解決內存不足問題的,以及分配出來的內存是如何管理的。 Dalvik虛擬機實現了一個dvmAllocObject函數。每當Dalvik虛擬機需要為對象分配內存時,就會調用函數dvmAllocObject。例如,當Dalvik虛擬機的解釋器遇到一個new指令時,它就會調用函數dvmAllocObject,如下所示: ~~~ HANDLE_OPCODE(OP_NEW_INSTANCE /*vAA, class@BBBB*/) { ClassObject* clazz; Object* newObj; EXPORT_PC(); vdst = INST_AA(inst); ref = FETCH(1); ...... clazz = dvmDexGetResolvedClass(methodClassDex, ref); if (clazz == NULL) { clazz = dvmResolveClass(curMethod->clazz, ref, false); ...... } ...... newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); ...... SET_REGISTER(vdst, (u4) newObj); } FINISH(2); OP_END ~~~ 這個代碼段定義在文件dalvik/vm/mterp/out/InterpC-portable.cpp中。 關于Dalvik虛擬機的解釋器的實現,可以參考[Dalvik虛擬機的運行過程分析](http://blog.csdn.net/luoshengyang/article/details/8914953)一文,上面這段代碼首先是找到要創建的對象的類型clazz,接著以其作為參數調用函數dvmAllocObject為要創建的對象分配內存。另外一個參數ALLOC_DONT_TRACK是告訴Dalvik虛擬機的堆管理器,要分配的對象是一個根集對象,不需要對它進行跟蹤。因為根集對象在GC時是會自動被追蹤處理的。 函數dvmAllocObject的實現如下所示: ~~~ Object* dvmAllocObject(ClassObject* clazz, int flags) { Object* newObj; assert(clazz != NULL); assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); /* allocate on GC heap; memory is zeroed out */ newObj = (Object*)dvmMalloc(clazz->objectSize, flags); if (newObj != NULL) { DVM_OBJECT_INIT(newObj, clazz); dvmTrackAllocation(clazz, clazz->objectSize); /* notify DDMS */ } return newObj; } ~~~ 這個函數定義在文件dalvik/vm/alloc/Alloc.cpp中。 函數dvmAllocObject調用函數dvmMalloc從Java堆中分配一塊指定大小的內存給新創建的對象使用。如果分配成功,那么接下來就先使用宏DVM_OBJECT_INIT來初始化新創建對對象的成員變量clazz,使得新創建的對象可以與某個特定的類關聯起來,接著再調用函數dvmTrackAllocation記錄當前的內存分配信息,以便通知DDMS。 函數dvmMalloc返回的只是一塊內存地址,這是沒有類型的。但是由于每一個Java對象都是從Object類繼承下來的,因此,函數dvmAllocObject可以將獲得的沒有類型的內存塊強制轉換為一個Object對象。 Object類的定義如下所示: ~~~ struct Object { /* ptr to class object */ ClassObject* clazz; /* * A word containing either a "thin" lock or a "fat" monitor. See * the comments in Sync.c for a description of its layout. */ u4 lock; }; ~~~ 這個類定義在文件dalvik/vm/oo/Object.h中。 Object類有兩個成員變量:clazz和lock。其中,成員變量clazz的類型為ClassObject,它對應于Java層的java.lang.Class類,用來描述對象所屬的類。成員變量lock是一個鎖,正是因為有了這個成員變量,在Java層中,每一個對象都可以當鎖使用。 理解了Object類的定義之后,我們繼續分析函數dvmMalloc的實現,如下所示: ~~~ void* dvmMalloc(size_t size, int flags) { void *ptr; dvmLockHeap(); /* Try as hard as possible to allocate some memory. */ ptr = tryMalloc(size); if (ptr != NULL) { /* We've got the memory. */ if (gDvm.allocProf.enabled) { Thread* self = dvmThreadSelf(); gDvm.allocProf.allocCount++; gDvm.allocProf.allocSize += size; if (self != NULL) { self->allocProf.allocCount++; self->allocProf.allocSize += size; } } } else { /* The allocation failed. */ if (gDvm.allocProf.enabled) { Thread* self = dvmThreadSelf(); gDvm.allocProf.failedAllocCount++; gDvm.allocProf.failedAllocSize += size; if (self != NULL) { self->allocProf.failedAllocCount++; self->allocProf.failedAllocSize += size; } } } dvmUnlockHeap(); if (ptr != NULL) { /* * If caller hasn't asked us not to track it, add it to the * internal tracking list. */ if ((flags & ALLOC_DONT_TRACK) == 0) { dvmAddTrackedAlloc((Object*)ptr, NULL); } } else { /* * The allocation failed; throw an OutOfMemoryError. */ throwOOME(); } return ptr; } ~~~ 這個函數定義在文件dalvik/vm/alloc/Heap.cpp中。 在Java堆分配內存前后,要對Java堆進行加鎖和解鎖,避免多個線程同時對Java堆進行操作。這分別是通過函數dvmLockHeap和dvmunlockHeap來實現的。真正執行內存分配的操作是通過調用另外一個函數tryMalloc來完成的。如果分配成功,則記錄當前線程成功分配的內存字節數和對象數等信息。否則的話,就記錄當前線程失敗分配的內存字節數和對象等信息。有了這些信息之后,我們就可以通過DDMS等工具來對應用程序的內存使用信息進行統計了。 最后,如果分配內存成功,并且參數flags的ALLOC_DONT_TRACK位設置為0,那么需要將新創建的對象增加到Dalvik虛擬機內部的一個引用表去。保存在這個內部引用表的對象在執行GC時,會添加到根集去,以便可以正確地判斷對象的存活。 另一方面,如果分配內存失敗,那么就是時候調用函數throwOOME拋出一個OOM異常了。 我們接下來繼續分析函數tryMalloc的實現,如下所示: ~~~ static void *tryMalloc(size_t size) { void *ptr; ...... 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; } ...... return NULL; } ~~~ 這個函數定義在文件dalvik/vm/alloc/Heap.cpp中。 函數tryMalloc的執行流程就如圖2所示: 1. 調用函數dvmHeapSourceAlloc在Java堆上分配指定大小的內存。如果分配成功,那么就將分配得到的地址直接返回給調用者了。函數dvmHeapSourceAlloc在不改變Java堆當前大小的前提下進行內存分配,這是屬于輕量級的內存分配動作。 2. 如果上一步內存分配失敗,這時候就需要執行一次GC了。不過如果GC線程已經在運行中,即gDvm.gcHeap->gcRunning的值等于true,那么就直接調用函數dvmWaitForConcurrentGcToComplete等到GC執行完成就是了。否則的話,就需要調用函數gcForMalloc來執行一次GC了,參數false表示不要回收軟引用對象引用的對象。 3. GC執行完畢后,再次調用函數dvmHeapSourceAlloc嘗試輕量級的內存分配操作。如果分配成功,那么就將分配得到的地址直接返回給調用者了。 4. 如果上一步內存分配失敗,這時候就得考慮先將Java堆的當前大小設置為Dalvik虛擬機啟動時指定的Java堆最大值,再進行內存分配了。這是通過調用函數dvmHeapSourceAllocAndGrow來實現的。 5. 如果調用函數dvmHeapSourceAllocAndGrow分配內存成功,則直接將分配得到的地址直接返回給調用者了。 6. 如果上一步內存分配還是失敗,這時候就得出狠招了。再次調用函數gcForMalloc來執行GC。參數true表示要回收軟引用對象引用的對象。 7. GC執行完畢,再次調用函數dvmHeapSourceAllocAndGrow進行內存分配。這是最后一次努力了,成功與事都到此為止。 這里涉及到的關鍵函數有三個,分別是dvmHeapSourceAlloc、dvmHeapSourceAllocAndGrow和gcForMalloc。后面一個我們在接下來一篇文章分析Dalvik虛擬機的垃圾收集過程時再分析。現在重點分析前面兩個函數。 函數dvmHeapSourceAlloc的實現如下所示: ~~~ void* dvmHeapSourceAlloc(size_t n) { HS_BOILERPLATE(); HeapSource *hs = gHs; Heap* heap = hs2heap(hs); if (heap->bytesAllocated + n > hs->softLimit) { ...... return NULL; } void* ptr; if (gDvm.lowMemoryMode) { ...... ptr = mspace_malloc(heap->msp, n); if (ptr == NULL) { return NULL; } uintptr_t zero_begin = (uintptr_t)ptr; uintptr_t zero_end = (uintptr_t)ptr + n; ...... uintptr_t begin = ALIGN_UP_TO_PAGE_SIZE(zero_begin); uintptr_t end = zero_end & ~(uintptr_t)(SYSTEM_PAGE_SIZE - 1); ...... if (begin < end) { ...... madvise((void*)begin, end - begin, MADV_DONTNEED); ...... memset((void*)end, 0, zero_end - end); ...... zero_end = begin; } memset((void*)zero_begin, 0, zero_end - zero_begin); } else { 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) { ...... dvmSignalCond(&gHs->gcThreadCond); } return ptr; } ~~~ 這個函數定義在文件dalvik/vm/alloc/HeapSource.cpp中。 從前面[Dalvik虛擬機Java堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/41581063)一文可知,gHs是一個全局變量,它指向一個HeapSource結構。在這個HeapSource結構中,有一個heaps數組,其中第一個元素描述的是Active堆,第二個元素描述的是Zygote堆。 通過宏hs2heap可以獲得HeapSource結構中的Active堆,保存在本地變量heap中。宏hs2heap的實現如下所示: ~~~ #define hs2heap(hs_) (&((hs_)->heaps[0])) ~~~ 這個宏定義在文件dalvik/vm/alloc/HeapSource.cpp中。 在前面[Dalvik虛擬機Java堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/41581063)一文中,我們解釋了Java堆有起始大小、最大值、增長上限值、最小空閑值、最大空閑值和目標利用率等參數。在Dalvik虛擬機內部,還有一個稱為軟限制(Soft Limit)的參數。堆軟限制是一個與堆目標利率相關的參數。 Java堆的Soft Limit開始的時候設置為最大允許的整數值。但是每一次GC之后,Dalvik虛擬機會根據Active堆已經分配的內存字節數、設定的堆目標利用率和Zygote堆的大小,重新計算Soft Limit,以及別外一個稱為理想大小(Ideal Size)的值。如果此時只有一個堆,即只有Active堆沒有Zygote堆,那么Soft Limit就等于Ideal Size。如果此時有兩個堆,那么Ideal Size就等于Zygote堆的大小再加上Soft Limit值,其中Soft Limit值就是此時Active堆的大小,它是根據Active堆已經分配的內存字節數和設定的堆目標利用率計算得到的。 這個Soft Limit值到底有什么用呢?它主要是用來限制Active堆無節制地增長到最大值的,而是要根據預先設定的堆目標利用率來控制Active有節奏地增長到最大值。這樣可以更有效地使用堆內存。想象一下,如果我們一開始Active堆的大小設置為最大值,那么就很有可能造成已分配的內存分布在一個很大的范圍。這樣隨著Dalvik虛擬機不斷地運行,Active堆的內存碎片就會越來越來重。相反,如果我們施加一個Soft Limit,那可以盡量地控制已分配的內存都位于較緊湊的范圍內。這樣就可以有效地減少碎片。 回到函數dvmHeapSourceAlloc中,參數n描述的是要分配的內存大小,而heap->bytesAllocated描述的是Active堆已經的內存大小。由于函數dvmHeapSourceAlloc是不允許增長Active堆的大小的,因此當(heap->bytesAllocated + n)的值大于Active堆的Soft Limit時,就直接返回一個NULL值表示分配內存失敗。 如果要分配的內存不會超過Active堆的Soft Limit,那么就要考慮Dalivk虛擬機在啟動時是否指定了低內存模式。我們可以通過-XX:LowMemoryMode選項來讓Dalvik虛擬機運行低內存模式下。在低內存模式和非低內存模塊中,對象內存的分配方式有所不同。 在低內存模式中,Dalvik虛擬機假設對象不會馬上就使用分配到的內存,因此,它就通過系統接口madvice和MADV_DONTNEED標志告訴內核,剛剛分配出去的內存在近期內不會使用,內核可以該內存對應的物理頁回收。當分配出去的內存被使用時,內核就會重新給它映射物理頁,這樣就可以做按需分配物理內存,適合在內存小的設備上運行。這里有三點需要注意。 * 第一點是Dalvik虛擬機要求分配給對象的內存初始化為0,但是在低內存模式中,是使用函數mspace_malloc來分配內存,該函數不會將分配的內存初始化為0,因此我們需要自己去初始化這塊內存。 * 第二點是對于被系統接口madvice標記為MADV_DONTNEED的內存,是不需要我們將它初始化為0的,一來是因為這是無用功(對應的物理而可能會被內核回收),二來是因為當這些內存在真正使用時,內核在為它們映射物理頁的同時,也會同時映射的物理頁初始為0。 * 第三點是在調用系統接口madvice時,指定的內存地址以及內存大小都必須以頁大小為邊界的,但是函數mspace_malloc分配出來的內存的地址只能保證對齊到8個字節,因此,我們是有可能不能將所有分配出來的內存都通過系統接口madvice標記為MADV_DONTNEED的。這時候對于不能標記為MADV_DONTNEED的內存,就需要調用memset來將它們初始化為0。 在非低內存模式中,處理的邏輯就簡單很多了,直接使用函數mspace_calloc在Active堆上分配指定的內存大小即可,同時該函數還會將分配的內存初始化為0,正好是可以滿足Dalvik虛擬機的要求。 注意,由于內存碎片的存在,即使是要分配的內存沒有超出Active堆的Soft Limit,在調用函數mspace_malloc和函數mspace_calloc的時候,仍然有可能出現無法成功分配內存的情況。在這種情況下,都直接返回一個NULL值給調用者。 在分配成功的情況下,函數dvmHeapSourceAlloc還需要做兩件事情。 第一件事情是調用函數countAllocation來計賬,它的實現如下所示: ~~~ static void countAllocation(Heap *heap, const void *ptr) { assert(heap->bytesAllocated < mspace_footprint(heap->msp)); heap->bytesAllocated += mspace_usable_size(ptr) + HEAP_SOURCE_CHUNK_OVERHEAD; heap->objectsAllocated++; HeapSource* hs = gDvm.gcHeap->heapSource; dvmHeapBitmapSetObjectBit(&hs->liveBits, ptr); assert(heap->bytesAllocated < mspace_footprint(heap->msp)); } ~~~ 這個函數定義在文件dalvik/vm/alloc/HeapSource.cpp中。 函數countAllocation要計的賬有三個: 1. 記錄Active堆當前已經分配的字節數。 2. 記錄Active堆當前已經分配的對象數。 3. 調用函數dvmHeapBitmapSetObjectBit將新分配的對象在Live Heap Bitmap上對應的位設置為1,也就是說將新創建的對象標記為是存活的。關于Live Heap Bitmap,可以參考前面[Dalvik虛擬機Java堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/41581063)一文。 回到函數dvmHeapSourceAlloc中,它需要做的第二件事情是檢查當前Active堆已經分配的字節數是否已經大于預先設定的Concurrent GC閥值heap->concurrentStartBytes。如果大于的話,那么就需要通知GC線程執行一次Concurrent GC。當然,如果當前GC線程已經在進行垃圾回收,那么就不用通知了。當gDvm.gcHeap->gcRunning的值等于true時,就表示GC線程正在進行垃圾回收。 這樣,函數dvmHeapSourceAlloc的實現就分析完成了,接下來我們繼續分析另外一個函數dvmHeapSourceAllocAndGrow的實現,如下所示: ~~~ 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; if (isSoftLimited(hs)) { ...... hs->softLimit = SIZE_MAX; ptr = dvmHeapSourceAlloc(n); if (ptr != NULL) { ...... snapIdealFootprint(); return ptr; } } ptr = heapAllocAndGrow(hs, heap, n); if (ptr != NULL) { ...... snapIdealFootprint(); } else { ...... setIdealFootprint(oldIdealSize); } return ptr; } ~~~ 這個函數定義在文件dalvik/vm/alloc/HeapSource.cpp中。 函數dvmHeapSourceAllocAndGrow首先是在不增加Active堆的前提下,調用我們前面分析的函數dvmHeapSourceAlloc來分配大小為n的內存。如果分配成功,那么就可以直接返回了。否則的話,繼續往前處理。 在繼續往前處理之前,先記錄一下當前Zygote堆和Active堆的大小之和oldIdealSize。這是因為后面我們可能會修改Active堆的大小。當修改了Active堆的大小,但是仍然不能成功分配大小為n的內存,那么就需要恢復之前Zygote堆和Active堆的大小。 如果Active堆設置有Soft Limit,那么函數isSoftLimited的返回值等于true。在這種情況下,先將Soft Limit去掉,再調用函數dvmHeapSourceAlloc來分配大小為n的內存。如果分配成功,那么在將分配得到的地址返回給調用者之前,需要調用函數snapIdealFootprint來修改Active堆的大小。也就是說,在去掉Active堆的Soft Limit之后,可以成功地分配到大小為n的內存,這時候就需要相應的增加Soft Limit的大小。 如果Active堆沒有設置Soft Limit,或者去掉Soft Limit之后,仍然不能成功地在Active堆上分配在大小為n的內存,那么這時候就得出大招了,它會調用函數heapAllocAndGrow將Java堆的大小設置為允許的最大值,然后再在Active堆上分配大小為n的內存。 最后,如果能成功分配到大小為n的內存,那么就調用函數snapIdealFootprint來重新設置Active堆的當前大小。否則的話,就調用函數setIdealFootprint來恢復之前Active堆的大小。這是因為雖然分配失敗,但是前面仍然做了修改Active堆大小的操作。 為了更好地理解函數dvmHeapSourceAllocAndGrow的實現,我們繼續分析一下涉及到的函數isSoftLimited、setIdealFootprint、snapIdealFootprint和heapAllocAndGrow的實現。 函數isSoftLimited的實現如下所示: ~~~ static bool isSoftLimited(const HeapSource *hs) { /* softLimit will be either SIZE_MAX or the limit for the * active mspace. idealSize can be greater than softLimit * if there is more than one heap. If there is only one * heap, a non-SIZE_MAX softLimit should always be the same * as idealSize. */ return hs->softLimit <= hs->idealSize; } ~~~ 這個函數定義在文件dalvik/vm/alloc/HeapSource.cpp中。 根據我們前面的分析,hs->softLimit描述的是Active堆的大小,而hs->idealSize描述的是Zygote堆和Active堆的大小之和。 當只有一個堆時,即只有Active堆時,如果設置了Soft Limit,那么它的大小總是等于Active堆的大小,即這時候hs->softLimit總是等于hs->idealSize。如果沒有設置Soft Limit,那么它的值會被設置為SIZE_MAX值,這會就會保證hs->softLimit大于hs->idealSize。也就是說,當只有一個堆時,函數isSoftLimited能正確的反映Active堆是否設置有Soft Limit。 當有兩個堆時,即Zygote堆和Active堆同時存在,那么如果設置有Soft Limit,那么它的值就總是等于Active堆的大小。由于hs->idealSize描述的是Zygote堆和Active堆的大小之和,因此就一定可以保證hs->softLimit小于等于hs->idealSize。如果沒有設置Soft Limit,即hs->softLimit的值等于SIZE_MAX,那么就一定可以保證hs->softLimit的值大于hs->idealSize的值。也就是說,當有兩個堆時,函數isSoftLimited也能正確的反映Active堆是否設置有Soft Limit。 函數setIdealFootprint的實現如下所示: ~~~ static void setIdealFootprint(size_t max) { HS_BOILERPLATE(); HeapSource *hs = gHs; size_t maximumSize = getMaximumSize(hs); if (max > maximumSize) { LOGI_HEAP("Clamp target GC heap from %zd.%03zdMB to %u.%03uMB", FRACTIONAL_MB(max), FRACTIONAL_MB(maximumSize)); max = maximumSize; } /* Convert max into a size that applies to the active heap. * Old heaps will count against the ideal size. */ size_t overhead = getSoftFootprint(false); size_t activeMax; if (overhead < max) { activeMax = max - overhead; } else { activeMax = 0; } setSoftLimit(hs, activeMax); hs->idealSize = max; } ~~~ 這個函數定義在文件dalvik/vm/alloc/HeapSource.cpp中。 函數setIdealFootprint的作用是要將Zygote堆和Active堆的大小之和設置為max。在設置之前,先檢查max值是否大于Java堆允許的最大值maximum。如果大于的話,那么就屬于將max的值修改為maximum。 接下為是以參數false來調用函數getSoftFootprint來獲得Zygote堆的大小overhead。如果max的值大于Zygote堆的大小overhead,那么從max中減去overhead,就可以得到Active堆的大小activeMax。如果max的值小于等于Zygote堆的大小overhead,那么就說明要將Active堆的大小activeMax設置為0。 最后,函數setIdealFootprint調用函數setSoftLimit設置Active堆的當前大小,并且將Zygote堆和Active堆的大小之和記錄在hs->idealSize中。 這里又涉及到兩個函數getSoftFootprint和setSoftLimit,我們同樣對它們進行分析。 函數getSoftFootprint的實現如下所示: ~~~ static size_t getSoftFootprint(bool includeActive) { HS_BOILERPLATE(); HeapSource *hs = gHs; size_t ret = oldHeapOverhead(hs, false); if (includeActive) { ret += hs->heaps[0].bytesAllocated; } return ret; } ~~~ 這個函數定義在文件dalvik/vm/alloc/HeapSource.cpp中。 函數getSoftFootprint首先調用函數oldHeapOverhead獲得Zygote堆的大小ret。當參數includeActive等于true時,就表示要返回的是Zygote堆的大小再加上Active堆當前已經分配的內存字節數的值。而當參數includeActive等于false時,要返回的僅僅是Zygote堆的大小。 函數oldHeapOverhead的實現如下所示: ~~~ static size_t oldHeapOverhead(const HeapSource *hs, bool includeActive) { size_t footprint = 0; size_t i; if (includeActive) { i = 0; } else { i = 1; } for (/* i = i */; i < hs->numHeaps; i++) { //TODO: include size of bitmaps? If so, don't use bitsLen, listen to .max footprint += mspace_footprint(hs->heaps[i].msp); } return footprint; } ~~~ 這個函數定義在文件dalvik/vm/alloc/HeapSource.cpp中。 從這里就可以看出,當參數includeActive等于true時,函數oldHeapOverhead返回的是Zygote堆和Active堆的大小之和,而當參數includeActive等于false時,函數oldHeapOverhead僅僅返回Zygote堆的大小。注意,hs->heaps[0]指向的是Active堆,而hs->heaps[1]指向的是Zygote堆。這一點可以參考前面[Dalvik虛擬機Java堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/41581063)一文。 回到函數setIdealFootprint中,我們繼續分析函數setSoftLimit的實現,如下所示: ~~~ static void setSoftLimit(HeapSource *hs, size_t softLimit) { ...... mspace msp = hs->heaps[0].msp; size_t currentHeapSize = mspace_footprint(msp); if (softLimit < currentHeapSize) { ...... mspace_set_footprint_limit(msp, currentHeapSize); hs->softLimit = softLimit; } else { ...... mspace_set_footprint_limit(msp, softLimit); hs->softLimit = SIZE_MAX; } } ~~~ 這個函數定義在文件dalvik/vm/alloc/HeapSource.cpp中。 函數setSoftLimit首先是獲得Active堆的當前大小currentHeapSize。如果參數softLimit的值小于Active堆的當前大小currentHeapSize,那么就意味著要給Active堆設置一個Soft Limit,這時候主要就是將參數softLimit的保存在hs->softLimit中。另一方面,如果參數softLimit的值大于等于Active堆的當前大小currentHeapSize,那么就意味著要去掉Active堆的Soft Limit,并且將Active堆的大小設置為參數softLimit的值。 回到函數dvmHeapSourceAlloc中,我們繼續分析最后兩個函數snapIdealFootprint和heapAllocAndGrow的實現, 函數snapIdealFootprint的實同如下所示: ~~~ static void snapIdealFootprint() { HS_BOILERPLATE(); setIdealFootprint(getSoftFootprint(true)); } ~~~ 這個函數定義在文件dalvik/vm/alloc/HeapSource.cpp中。 函數snapIdealFootprint通過調用前面分析的函數getSoftFootprint和setIdealFootprint來調整Active堆的大小以及Soft Limit值。回憶一下函數dvmHeapSourceAlloc調用snapIdealFootprint的情景,分別是在修改了Active堆的Soft Limit或者將Active堆的大小設置為允許的最大值,并且成功在Active堆分配了指定大小的內存之后進行的。這樣就需要調用函數snapIdealFootprint將Active堆的大小設置為實際使用的大小(而不是允許的最大值),以及重新設置Soft Limit值。 函數heapAllocAndGrow的實現如下所示: ~~~ static void* heapAllocAndGrow(HeapSource *hs, Heap *heap, size_t n) { ...... size_t max = heap->maximumSize; mspace_set_footprint_limit(heap->msp, max); void* ptr = dvmHeapSourceAlloc(n); ...... mspace_set_footprint_limit(heap->msp, mspace_footprint(heap->msp)); return ptr; } ~~~ 這個函數定義在文件dalvik/vm/alloc/HeapSource.cpp中。 函數heapAllocAndGrow使用最激進的辦法來在參數heap描述的堆上分配內存。在我們這個情景中,參數heap描述的就是Active堆上。它首先將Active堆的大小設置為允許的最大值,接著再調用函數dvmHeapSourceAlloc在上面分配大小為n的內存。接著再通過函數mspace_footprint獲得分配了n個字節之后Active堆的大小,并且將該值設置為Active堆的當前大小限制。這就相當于是將Active堆的當前大小限制值從允許設置的最大值減少為一個剛剛合適的值。 至此,我們就分析完成了Dalvik虛擬機為新創建的對象分配內存的過程。只有充分理解了對象內存的分配過程之后,我們才能夠更好地理解對象內存的釋放過程,也就是Dalvik虛擬機的垃圾收集過程。在接下來的一篇文章中,我們就將詳細分析Dalvik虛擬機的垃圾收集過程,敬請關注!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看