Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的過(guò)程分析_第1頁(yè)
Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的過(guò)程分析_第2頁(yè)
Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的過(guò)程分析_第3頁(yè)
Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的過(guò)程分析_第4頁(yè)
Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的過(guò)程分析_第5頁(yè)
已閱讀5頁(yè),還剩12頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的過(guò)程分析在前面一文中,我們分析了Dalvik虛擬機(jī)創(chuàng)建Java堆的過(guò)程。有了Java堆之后,Dalvik虛擬機(jī)就可以在上面為對(duì)象分配內(nèi)存了。在Java堆為對(duì)象分配內(nèi)存需要解決內(nèi)存碎片和內(nèi)存不足兩個(gè)問(wèn)題。要解決內(nèi)存碎片問(wèn)題,就要找到一塊大小最合適的空閑內(nèi)存分配給對(duì)象使用。而內(nèi)存不足有可能是內(nèi)存配額用完引起的,也有可能是垃圾沒(méi)有及時(shí)回收引起的,要區(qū)別對(duì)待。本文就詳細(xì)分析Dalvik虛擬機(jī)是如何解決這些問(wèn)題的。內(nèi)存碎片問(wèn)題其實(shí)是一個(gè)通用的問(wèn)題,不單止Dalvik虛擬機(jī)在Java堆為對(duì)象分配內(nèi)存時(shí)會(huì)遇到,C庫(kù)的malloc函數(shù)在分配內(nèi)存時(shí)也會(huì)遇到。Android系統(tǒng)使用的C庫(kù)bionic使用了寫的dlmalloc內(nèi)存分配器。也就是說(shuō),我們調(diào)用函數(shù)malloc的時(shí)候,使用的是dlmalloc內(nèi)存分配器來(lái)分配內(nèi)存。這是一個(gè)成熟的內(nèi)存分配器,可以很好地解決內(nèi)存碎片問(wèn)題。關(guān)于dlmalloc內(nèi)存分配器的設(shè)計(jì),可以參考這篇文章:。前面一文提到,Dalvik虛擬機(jī)的Java堆的底層實(shí)現(xiàn)是一塊匿名共享內(nèi)存,并且將其抽象為C庫(kù)的一個(gè)mspace,如圖1所示:于是,Dalvik虛擬機(jī)就很機(jī)智地利用C庫(kù)里面的dlmalloc內(nèi)存分配器來(lái)解決內(nèi)存碎片問(wèn)題!為了應(yīng)對(duì)可能面臨的內(nèi)存不足問(wèn)題,Dalvik虛擬機(jī)采用一種漸進(jìn)的方法來(lái)為對(duì)象分配內(nèi)存,直到盡了最大努力,如圖2所示:接下來(lái),我們就詳細(xì)分析這個(gè)過(guò)程,以便可以了解Dalvik虛擬機(jī)是如何解決內(nèi)存不足問(wèn)題的,以及分配出來(lái)的內(nèi)存是如何管理的。Dalvik虛擬機(jī)實(shí)現(xiàn)了一個(gè)dvmAllocObject函數(shù)。每當(dāng)Dalvik虛擬機(jī)需要為對(duì)象分配內(nèi)存時(shí),就會(huì)調(diào)用函數(shù)dvmAllocObject。例如,當(dāng)Dalvik虛擬機(jī)的解釋器遇到一個(gè)new指令時(shí),它就會(huì)調(diào)用函數(shù)dvmAllocObject,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片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這個(gè)代碼段定義在文件dalvik/vm/mterp/out/InterpC-portable.cpp中。關(guān)于Dalvik虛擬機(jī)的解釋器的實(shí)現(xiàn),可以參考一文,上面這段代碼首先是找到要?jiǎng)?chuàng)建的對(duì)象的類型clazz,接著以其作為參數(shù)調(diào)用函數(shù)dvmAllocObject為要?jiǎng)?chuàng)建的對(duì)象分配內(nèi)存。另外一個(gè)參數(shù)ALLOC_DONT_TRACK是告訴Dalvik虛擬機(jī)的堆管理器,要分配的對(duì)象是一個(gè)根集對(duì)象,不需要對(duì)它進(jìn)行跟蹤。因?yàn)楦瘜?duì)象在GC時(shí)是會(huì)自動(dòng)被追蹤處理的。函數(shù)dvmAllocObject的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片Object*dvmAllocObject(ClassObject*clazz,intflags){Object*newObj;assert(clazz!=NULL);assert(dvmIsClassInitialized(clazz)||dvmIsClassInitializing(clazz));/*allocateonGCheap;memoryiszeroedout*/newObj=(Object*)dvmMalloc(clazz->objectSize,flags);if(newObj!=NULL){DVM_OBJECT_INIT(newObj,clazz);dvmTrackAllocation(clazz,clazz->objectSize);/*notifyDDMS*/}returnnewObj;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Alloc.cpp中。函數(shù)dvmAllocObject調(diào)用函數(shù)dvmMalloc從Java堆中分配一塊指定大小的內(nèi)存給新創(chuàng)建的對(duì)象使用。如果分配成功,那么接下來(lái)就先使用宏DVM_OBJECT_INIT來(lái)初始化新創(chuàng)建對(duì)對(duì)象的成員變量clazz,使得新創(chuàng)建的對(duì)象可以與某個(gè)特定的類關(guān)聯(lián)起來(lái),接著再調(diào)用函數(shù)dvmTrackAllocation記錄當(dāng)前的內(nèi)存分配信息,以便通知DDMS。函數(shù)dvmMalloc返回的只是一塊內(nèi)存地址,這是沒(méi)有類型的。但是由于每一個(gè)Java對(duì)象都是從Object類繼承下來(lái)的,因此,函數(shù)dvmAllocObject可以將獲得的沒(méi)有類型的內(nèi)存塊強(qiáng)制轉(zhuǎn)換為一個(gè)Object對(duì)象。Object類的定義如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片structObject{/*ptrtoclassobject*/ClassObject*clazz;/**Awordcontainingeithera"thin"lockora"fat"monitor.See*thecommentsinSync.cforadescriptionofitslayout.*/u4lock;};這個(gè)類定義在文件dalvik/vm/oo/Object.h中。Object類有兩個(gè)成員變量:clazz和lock。其中,成員變量clazz的類型為ClassObject,它對(duì)應(yīng)于Java層的java.lang.Class類,用來(lái)描述對(duì)象所屬的類。成員變量lock是一個(gè)鎖,正是因?yàn)橛辛诉@個(gè)成員變量,在Java層中,每一個(gè)對(duì)象都可以當(dāng)鎖使用。理解了Object類的定義之后,我們繼續(xù)分析函數(shù)dvmMalloc的實(shí)現(xiàn),如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片void*dvmMalloc(size_tsize,intflags){void*ptr;dvmLockHeap();/*Tryashardaspossibletoallocatesomememory.*/ptr=tryMalloc(size);if(ptr!=NULL){/*We'vegotthememory.*/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{/*Theallocationfailed.*/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){/**Ifcallerhasn'taskedusnottotrackit,addittothe*internaltrackinglist.*/if((flags&ALLOC_DONT_TRACK)==0){dvmAddTrackedAlloc((Object*)ptr,NULL);}}else{/**Theallocationfailed;throwanOutOfMemoryError.*/throwOOME();}returnptr;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Heap.cpp中。在Java堆分配內(nèi)存前后,要對(duì)Java堆進(jìn)行加鎖和解鎖,避免多個(gè)線程同時(shí)對(duì)Java堆進(jìn)行操作。這分別是通過(guò)函數(shù)dvmLockHeap和dvmunlockHeap來(lái)實(shí)現(xiàn)的。真正執(zhí)行內(nèi)存分配的操作是通過(guò)調(diào)用另外一個(gè)函數(shù)tryMalloc來(lái)完成的。如果分配成功,則記錄當(dāng)前線程成功分配的內(nèi)存字節(jié)數(shù)和對(duì)象數(shù)等信息。否則的話,就記錄當(dāng)前線程失敗分配的內(nèi)存字節(jié)數(shù)和對(duì)象等信息。有了這些信息之后,我們就可以通過(guò)DDMS等工具來(lái)對(duì)應(yīng)用程序的內(nèi)存使用信息進(jìn)行統(tǒng)計(jì)了。最后,如果分配內(nèi)存成功,并且參數(shù)flags的ALLOC_DONT_TRACK位設(shè)置為0,那么需要將新創(chuàng)建的對(duì)象增加到Dalvik虛擬機(jī)內(nèi)部的一個(gè)引用表去。保存在這個(gè)內(nèi)部引用表的對(duì)象在執(zhí)行GC時(shí),會(huì)添加到根集去,以便可以正確地判斷對(duì)象的存活。另一方面,如果分配內(nèi)存失敗,那么就是時(shí)候調(diào)用函數(shù)throwOOME拋出一個(gè)OOM異常了。我們接下來(lái)繼續(xù)分析函數(shù)tryMalloc的實(shí)現(xiàn),如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticvoid*tryMalloc(size_tsize){void*ptr;ptr=dvmHeapSourceAlloc(size);if(ptr!=NULL){returnptr;}if(gDvm.gcHeap->gcRunning){dvmWaitForConcurrentGcToComplete();}else{gcForMalloc(false);}ptr=dvmHeapSourceAlloc(size);if(ptr!=NULL){returnptr;}ptr=dvmHeapSourceAllocAndGrow(size);if(ptr!=NULL){returnptr;}gcForMalloc(true);ptr=dvmHeapSourceAllocAndGrow(size);if(ptr!=NULL){returnptr;}returnNULL;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Heap.cpp中。函數(shù)tryMalloc的執(zhí)行流程就如圖2所示:1.調(diào)用函數(shù)dvmHeapSourceAlloc在Java堆上分配指定大小的內(nèi)存。如果分配成功,那么就將分配得到的地址直接返回給調(diào)用者了。函數(shù)dvmHeapSourceAlloc在不改變Java堆當(dāng)前大小的前提下進(jìn)行內(nèi)存分配,這是屬于輕量級(jí)的內(nèi)存分配動(dòng)作。2.如果上一步內(nèi)存分配失敗,這時(shí)候就需要執(zhí)行一次GC了。不過(guò)如果GC線程已經(jīng)在運(yùn)行中,即gDvm.gcHeap->gcRunning的值等于true,那么就直接調(diào)用函數(shù)dvmWaitForConcurrentGcToComplete等到GC執(zhí)行完成就是了。否則的話,就需要調(diào)用函數(shù)gcForMalloc來(lái)執(zhí)行一次GC了,參數(shù)false表示不要回收軟引用對(duì)象引用的對(duì)象。3.GC執(zhí)行完畢后,再次調(diào)用函數(shù)dvmHeapSourceAlloc嘗試輕量級(jí)的內(nèi)存分配操作。如果分配成功,那么就將分配得到的地址直接返回給調(diào)用者了。4.如果上一步內(nèi)存分配失敗,這時(shí)候就得考慮先將Java堆的當(dāng)前大小設(shè)置為Dalvik虛擬機(jī)啟動(dòng)時(shí)指定的Java堆最大值,再進(jìn)行內(nèi)存分配了。這是通過(guò)調(diào)用函數(shù)dvmHeapSourceAllocAndGrow來(lái)實(shí)現(xiàn)的。5.如果調(diào)用函數(shù)dvmHeapSourceAllocAndGrow分配內(nèi)存成功,則直接將分配得到的地址直接返回給調(diào)用者了。6.如果上一步內(nèi)存分配還是失敗,這時(shí)候就得出狠招了。再次調(diào)用函數(shù)gcForMalloc來(lái)執(zhí)行GC。參數(shù)true表示要回收軟引用對(duì)象引用的對(duì)象。7.GC執(zhí)行完畢,再次調(diào)用函數(shù)dvmHeapSourceAllocAndGrow進(jìn)行內(nèi)存分配。這是最后一次努力了,成功與事都到此為止。這里涉及到的關(guān)鍵函數(shù)有三個(gè),分別是dvmHeapSourceAlloc、dvmHeapSourceAllocAndGrow和gcForMalloc。后面一個(gè)我們?cè)诮酉聛?lái)一篇文章分析Dalvik虛擬機(jī)的垃圾收集過(guò)程時(shí)再分析。現(xiàn)在重點(diǎn)分析前面兩個(gè)函數(shù)。函數(shù)dvmHeapSourceAlloc的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片void*dvmHeapSourceAlloc(size_tn){HS_BOILERPLATE();HeapSource*hs=gHs;Heap*heap=hs2heap(hs);if(heap->bytesAllocated+n>hs->softLimit){returnNULL;}void*ptr;if(gDvm.lowMemoryMode){ptr=mspace_malloc(heap->msp,n);if(ptr==NULL){returnNULL;}uintptr_tzero_begin=(uintptr_t)ptr;uintptr_tzero_end=(uintptr_t)ptr+n;uintptr_tbegin=ALIGN_UP_TO_PAGE_SIZE(zero_begin);uintptr_tend=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){returnNULL;}}countAllocation(heap,ptr);if(gDvm.gcHeap->gcRunning||!hs->hasGcThread){returnptr;}if(heap->bytesAllocated>heap->concurrentStartBytes){dvmSignalCond(&gHs->gcThreadCond);}returnptr;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。從前面一文可知,gHs是一個(gè)全局變量,它指向一個(gè)HeapSource結(jié)構(gòu)。在這個(gè)HeapSource結(jié)構(gòu)中,有一個(gè)heaps數(shù)組,其中第一個(gè)元素描述的是Active堆,第二個(gè)元素描述的是Zygote堆。通過(guò)宏hs2heap可以獲得HeapSource結(jié)構(gòu)中的Active堆,保存在本地變量heap中。宏hs2heap的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片#definehs2heap(hs_)(&((hs_)->heaps[0]))這個(gè)宏定義在文件dalvik/vm/alloc/HeapSource.cpp中。在前面一文中,我們解釋了Java堆有起始大小、最大值、增長(zhǎng)上限值、最小空閑值、最大空閑值和目標(biāo)利用率等參數(shù)。在Dalvik虛擬機(jī)內(nèi)部,還有一個(gè)稱為軟限制(SoftLimit)的參數(shù)。堆軟限制是一個(gè)與堆目標(biāo)利率相關(guān)的參數(shù)。Java堆的SoftLimit開(kāi)始的時(shí)候設(shè)置為最大允許的整數(shù)值。但是每一次GC之后,Dalvik虛擬機(jī)會(huì)根據(jù)Active堆已經(jīng)分配的內(nèi)存字節(jié)數(shù)、設(shè)定的堆目標(biāo)利用率和Zygote堆的大小,重新計(jì)算SoftLimit,以及別外一個(gè)稱為理想大小(IdealSize)的值。如果此時(shí)只有一個(gè)堆,即只有Active堆沒(méi)有Zygote堆,那么SoftLimit就等于IdealSize。如果此時(shí)有兩個(gè)堆,那么IdealSize就等于Zygote堆的大小再加上SoftLimit值,其中SoftLimit值就是此時(shí)Active堆的大小,它是根據(jù)Active堆已經(jīng)分配的內(nèi)存字節(jié)數(shù)和設(shè)定的堆目標(biāo)利用率計(jì)算得到的。這個(gè)SoftLimit值到底有什么用呢?它主要是用來(lái)限制Active堆無(wú)節(jié)制地增長(zhǎng)到最大值的,而是要根據(jù)預(yù)先設(shè)定的堆目標(biāo)利用率來(lái)控制Active有節(jié)奏地增長(zhǎng)到最大值。這樣可以更有效地使用堆內(nèi)存。想象一下,如果我們一開(kāi)始Active堆的大小設(shè)置為最大值,那么就很有可能造成已分配的內(nèi)存分布在一個(gè)很大的范圍。這樣隨著Dalvik虛擬機(jī)不斷地運(yùn)行,Active堆的內(nèi)存碎片就會(huì)越來(lái)越來(lái)重。相反,如果我們施加一個(gè)SoftLimit,那可以盡量地控制已分配的內(nèi)存都位于較緊湊的范圍內(nèi)。這樣就可以有效地減少碎片?;氐胶瘮?shù)dvmHeapSourceAlloc中,參數(shù)n描述的是要分配的內(nèi)存大小,而heap->bytesAllocated描述的是Active堆已經(jīng)的內(nèi)存大小。由于函數(shù)dvmHeapSourceAlloc是不允許增長(zhǎng)Active堆的大小的,因此當(dāng)(heap->bytesAllocated+n)的值大于Active堆的SoftLimit時(shí),就直接返回一個(gè)NULL值表示分配內(nèi)存失敗。如果要分配的內(nèi)存不會(huì)超過(guò)Active堆的SoftLimit,那么就要考慮Dalivk虛擬機(jī)在啟動(dòng)時(shí)是否指定了低內(nèi)存模式。我們可以通過(guò)-XX:LowMemoryMode選項(xiàng)來(lái)讓Dalvik虛擬機(jī)運(yùn)行低內(nèi)存模式下。在低內(nèi)存模式和非低內(nèi)存模塊中,對(duì)象內(nèi)存的分配方式有所不同。在低內(nèi)存模式中,Dalvik虛擬機(jī)假設(shè)對(duì)象不會(huì)馬上就使用分配到的內(nèi)存,因此,它就通過(guò)系統(tǒng)接口madvice和MADV_DONTNEED標(biāo)志告訴內(nèi)核,剛剛分配出去的內(nèi)存在近期內(nèi)不會(huì)使用,內(nèi)核可以該內(nèi)存對(duì)應(yīng)的物理頁(yè)回收。當(dāng)分配出去的內(nèi)存被使用時(shí),內(nèi)核就會(huì)重新給它映射物理頁(yè),這樣就可以做按需分配物理內(nèi)存,適合在內(nèi)存小的設(shè)備上運(yùn)行。這里有三點(diǎn)需要注意。第一點(diǎn)是Dalvik虛擬機(jī)要求分配給對(duì)象的內(nèi)存初始化為0,但是在低內(nèi)存模式中,是使用函數(shù)mspace_malloc來(lái)分配內(nèi)存,該函數(shù)不會(huì)將分配的內(nèi)存初始化為0,因此我們需要自己去初始化這塊內(nèi)存。第二點(diǎn)是對(duì)于被系統(tǒng)接口madvice標(biāo)記為MADV_DONTNEED的內(nèi)存,是不需要我們將它初始化為0的,一來(lái)是因?yàn)檫@是無(wú)用功(對(duì)應(yīng)的物理而可能會(huì)被內(nèi)核回收),二來(lái)是因?yàn)楫?dāng)這些內(nèi)存在真正使用時(shí),內(nèi)核在為它們映射物理頁(yè)的同時(shí),也會(huì)同時(shí)映射的物理頁(yè)初始為0。第三點(diǎn)是在調(diào)用系統(tǒng)接口madvice時(shí),指定的內(nèi)存地址以及內(nèi)存大小都必須以頁(yè)大小為邊界的,但是函數(shù)mspace_malloc分配出來(lái)的內(nèi)存的地址只能保證對(duì)齊到8個(gè)字節(jié),因此,我們是有可能不能將所有分配出來(lái)的內(nèi)存都通過(guò)系統(tǒng)接口madvice標(biāo)記為MADV_DONTNEED的。這時(shí)候?qū)τ诓荒軜?biāo)記為MADV_DONTNEED的內(nèi)存,就需要調(diào)用memset來(lái)將它們初始化為0。在非低內(nèi)存模式中,處理的邏輯就簡(jiǎn)單很多了,直接使用函數(shù)mspace_calloc在Active堆上分配指定的內(nèi)存大小即可,同時(shí)該函數(shù)還會(huì)將分配的內(nèi)存初始化為0,正好是可以滿足Dalvik虛擬機(jī)的要求。注意,由于內(nèi)存碎片的存在,即使是要分配的內(nèi)存沒(méi)有超出Active堆的SoftLimit,在調(diào)用函數(shù)mspace_malloc和函數(shù)mspace_calloc的時(shí)候,仍然有可能出現(xiàn)無(wú)法成功分配內(nèi)存的情況。在這種情況下,都直接返回一個(gè)NULL值給調(diào)用者。在分配成功的情況下,函數(shù)dvmHeapSourceAlloc還需要做兩件事情。第一件事情是調(diào)用函數(shù)countAllocation來(lái)計(jì)賬,它的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticvoidcountAllocation(Heap*heap,constvoid*ptr){assert(heap->bytesAllocated<mspace_footprint(heap->msp));heap->bytesAllocated+=mspace_usable_size(ptr)+HEAP_SOURCE_CHUNK_OVERHEAD;heap->objectsAated++;HeapSource*hs=gDvm.gcHeap->heapSource;dvmHeapBitmapSetObjectBit(&hs->liveBits,ptr);assert(heap->bytesAllocated<mspace_footprint(heap->msp));}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。函數(shù)countAllocation要計(jì)的賬有三個(gè):1.記錄Active堆當(dāng)前已經(jīng)分配的字節(jié)數(shù)。2.記錄Active堆當(dāng)前已經(jīng)分配的對(duì)象數(shù)。3.調(diào)用函數(shù)dvmHeapBitmapSetObjectBit將新分配的對(duì)象在LiveHeapBitmap上對(duì)應(yīng)的位設(shè)置為1,也就是說(shuō)將新創(chuàng)建的對(duì)象標(biāo)記為是存活的。關(guān)于LiveHeapBitmap,可以參考前面一文?;氐胶瘮?shù)dvmHeapSourceAlloc中,它需要做的第二件事情是檢查當(dāng)前Active堆已經(jīng)分配的字節(jié)數(shù)是否已經(jīng)大于預(yù)先設(shè)定的ConcurrentGC閥值heap->concurrentStartBytes。如果大于的話,那么就需要通知GC線程執(zhí)行一次ConcurrentGC。當(dāng)然,如果當(dāng)前GC線程已經(jīng)在進(jìn)行垃圾回收,那么就不用通知了。當(dāng)gDvm.gcHeap->gcRunning的值等于true時(shí),就表示GC線程正在進(jìn)行垃圾回收。這樣,函數(shù)dvmHeapSourceAlloc的實(shí)現(xiàn)就分析完成了,接下來(lái)我們繼續(xù)分析另外一個(gè)函數(shù)dvmHeapSourceAllocAndGrow的實(shí)現(xiàn),如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片void*dvmHeapSourceAllocAndGrow(size_tn){HeapSource*hs=gHs;Heap*heap=hs2heap(hs);void*ptr=dvmHeapSourceAlloc(n);if(ptr!=NULL){returnptr;}size_toldIdealSize=hs->idealSize;if(isSoftLimited(hs)){hs->softLimit=SIZE_MAX;ptr=dvmHeapSourceAlloc(n);if(ptr!=NULL){snapIdealFootprint();returnptr;}}ptr=heapAllocAndGrow(hs,heap,n);if(ptr!=NULL){snapIdealFootprint();}else{setIdealFootprint(oldIdealSize);}returnptr;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。函數(shù)dvmHeapSourceAllocAndGrow首先是在不增加Active堆的前提下,調(diào)用我們前面分析的函數(shù)dvmHeapSourceAlloc來(lái)分配大小為n的內(nèi)存。如果分配成功,那么就可以直接返回了。否則的話,繼續(xù)往前處理。在繼續(xù)往前處理之前,先記錄一下當(dāng)前Zygote堆和Active堆的大小之和oldIdealSize。這是因?yàn)楹竺嫖覀兛赡軙?huì)修改Active堆的大小。當(dāng)修改了Active堆的大小,但是仍然不能成功分配大小為n的內(nèi)存,那么就需要恢復(fù)之前Zygote堆和Active堆的大小。如果Active堆設(shè)置有SoftLimit,那么函數(shù)isSoftLimited的返回值等于true。在這種情況下,先將SoftLimit去掉,再調(diào)用函數(shù)dvmHeapSourceAlloc來(lái)分配大小為n的內(nèi)存。如果分配成功,那么在將分配得到的地址返回給調(diào)用者之前,需要調(diào)用函數(shù)snapIdealFootprint來(lái)修改Active堆的大小。也就是說(shuō),在去掉Active堆的SoftLimit之后,可以成功地分配到大小為n的內(nèi)存,這時(shí)候就需要相應(yīng)的增加SoftLimit的大小。如果Active堆沒(méi)有設(shè)置SoftLimit,或者去掉SoftLimit之后,仍然不能成功地在Active堆上分配在大小為n的內(nèi)存,那么這時(shí)候就得出大招了,它會(huì)調(diào)用函數(shù)heapAllocAndGrow將Java堆的大小設(shè)置為允許的最大值,然后再在Active堆上分配大小為n的內(nèi)存。最后,如果能成功分配到大小為n的內(nèi)存,那么就調(diào)用函數(shù)snapIdealFootprint來(lái)重新設(shè)置Active堆的當(dāng)前大小。否則的話,就調(diào)用函數(shù)setIdealFootprint來(lái)恢復(fù)之前Active堆的大小。這是因?yàn)殡m然分配失敗,但是前面仍然做了修改Active堆大小的操作。為了更好地理解函數(shù)dvmHeapSourceAllocAndGrow的實(shí)現(xiàn),我們繼續(xù)分析一下涉及到的函數(shù)isSoftLimited、setIdealFootprint、snapIdealFootprint和heapAllocAndGrow的實(shí)現(xiàn)。函數(shù)isSoftLimited的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticboolisSoftLimited(constHeapSource*hs){/*softLimitwillbeeitherSIZE_MAXorthelimitforthe*activemspace.idealSizecanbegreaterthansoftLimit*ifthereismorethanoneheap.Ifthereisonlyone*heap,anon-SIZE_MAXsoftLimitshouldalwaysbethesame*asidealSize.*/returnhs->softLimit<=hs->idealSize;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。根據(jù)我們前面的分析,hs->softLimit描述的是Active堆的大小,而hs->idealSize描述的是Zygote堆和Active堆的大小之和。當(dāng)只有一個(gè)堆時(shí),即只有Active堆時(shí),如果設(shè)置了SoftLimit,那么它的大小總是等于Active堆的大小,即這時(shí)候hs->softLimit總是等于hs->idealSize。如果沒(méi)有設(shè)置SoftLimit,那么它的值會(huì)被設(shè)置為SIZE_MAX值,這會(huì)就會(huì)保證hs->softLimit大于hs->idealSize。也就是說(shuō),當(dāng)只有一個(gè)堆時(shí),函數(shù)isSoftLimited能正確的反映Active堆是否設(shè)置有SoftLimit。當(dāng)有兩個(gè)堆時(shí),即Zygote堆和Active堆同時(shí)存在,那么如果設(shè)置有SoftLimit,那么它的值就總是等于Active堆的大小。由于hs->idealSize描述的是Zygote堆和Active堆的大小之和,因此就一定可以保證hs->softLimit小于等于hs->idealSize。如果沒(méi)有設(shè)置SoftLimit,即hs->softLimit的值等于SIZE_MAX,那么就一定可以保證hs->softLimit的值大于hs->idealSize的值。也就是說(shuō),當(dāng)有兩個(gè)堆時(shí),函數(shù)isSoftLimited也能正確的反映Active堆是否設(shè)置有SoftLimit。函數(shù)setIdealFootprint的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticvoidsetIdealFootprint(size_tmax){HS_BOILERPLATE();HeapSource*hs=gHs;size_tmaximumSize=getMaximumSize(hs);if(max>maximumSize){LOGI_HEAP("ClamptargetGCheapfrom%zd.%03zdMBto%u.%03uMB",FRACTIONAL_MB(max),FRACTIONAL_MB(maximumSize));max=maximumSize;}/*Convertmaxintoasizethatappliestotheactiveheap.*Oldheapswillcountagainsttheidealsize.*/size_toverhead=getSoftFootprint(false);size_tactiveMax;if(overhead<max){activeMax=max-overhead;}else{activeMax=0;}setSoftLimit(hs,activeMax);hs->idealSize=max;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。函數(shù)setIdealFootprint的作用是要將Zygote堆和Active堆的大小之和設(shè)置為max。在設(shè)置之前,先檢查max值是否大于Java堆允許的最大值maximum。如果大于的話,那么就屬于將max的值修改為maximum。接下為是以參數(shù)false來(lái)調(diào)用函數(shù)getSoftFootprint來(lái)獲得Zygote堆的大小overhead。如果max的值大于Zygote堆的大小overhead,那么從max中減去overhead,就可以得到Active堆的大小activeMax。如果max的值小于等于Zygote堆的大小overhead,那么就說(shuō)明要將Active堆的大小activeMax設(shè)置為0。最后,函數(shù)setIdealFootprint調(diào)用函數(shù)setSoftLimit設(shè)置Active堆的當(dāng)前大小,并且將Zygote堆和Active堆的大小之和記錄在hs->idealSize中。這里又涉及到兩個(gè)函數(shù)getSoftFootprint和setSoftLimit,我們同樣對(duì)它們進(jìn)行分析。函數(shù)getSoftFootprint的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticsize_tgetSoftFootprint(boolincludeActive){HS_BOILERPLATE();HeapSource*hs=gHs;size_tret=oldHeapOverhead(hs,false);if(includeActive){ret+=hs->heaps[0].bytesAllocated;}returnret;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。函數(shù)getSoftFootprint首先調(diào)用函數(shù)oldHeapOverhead獲得Zygote堆的大小ret。當(dāng)參數(shù)includeActive等于true時(shí),就表示要返回的是Zygote堆的大小再加上Active堆當(dāng)前已經(jīng)分配的內(nèi)存字節(jié)數(shù)的值。而當(dāng)參數(shù)includeActive等于false時(shí),要返回的僅僅是Zygote堆的大小。函數(shù)oldHeapOverhead的實(shí)現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticsize_toldHeapOverhead(constHeapSource*hs,boolincludeActive){size_tfootprint=0;size_ti;if(includeActive){i=0;}else{i=1;}for(/*i=i*/;i<hs->numHeaps;i++){//TODO:includesizeofbitmaps?Ifso,don'tusebitsLen,listento.maxfootprint+=mspace_footprint(hs->heaps[i].msp);}returnfootprint;}這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。從這里就可以看出,當(dāng)參數(shù)includeActive等于true時(shí),函數(shù)oldHeapOverhead返回的是Zygote堆和Active堆的大小之和,而當(dāng)參數(shù)includeActive等于false時(shí),函數(shù)oldHeapOverhead僅僅返回Zygote堆的大小。注意,hs->heaps[0]指向的是Active堆,而hs->heaps[1]指向的是Zygote堆。這一點(diǎn)可以參考前面一文。回到函數(shù)setIdealFootprint中,我們繼續(xù)分析函數(shù)setSoftLimit的實(shí)現(xiàn),如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticvoidsetSoftLimit(HeapSource*hs,size_tsoftLimit){mspacemsp=hs->heaps[0].msp;size_tcurrentHeapSize=mspace_footprint(msp);if(softLimit<currentHeapSize){

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論