Dalvik虛擬機(jī)Java堆創(chuàng)建過(guò)程分析_第1頁(yè)
Dalvik虛擬機(jī)Java堆創(chuàng)建過(guò)程分析_第2頁(yè)
Dalvik虛擬機(jī)Java堆創(chuàng)建過(guò)程分析_第3頁(yè)
Dalvik虛擬機(jī)Java堆創(chuàng)建過(guò)程分析_第4頁(yè)
Dalvik虛擬機(jī)Java堆創(chuàng)建過(guò)程分析_第5頁(yè)
已閱讀5頁(yè),還剩20頁(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ī)Java堆創(chuàng)建過(guò)程分析

使用C/C++開(kāi)發(fā)應(yīng)用程序最令頭痛的問(wèn)題就是內(nèi)存管理,慎不留神,要么內(nèi)存泄漏,要么內(nèi)

存破壞。虛擬機(jī)要解決的問(wèn)題之一就是幫助應(yīng)用程序自動(dòng)分配和釋放內(nèi)存。為了達(dá)到這個(gè)目

的,虛擬機(jī)在啟動(dòng)的時(shí)候向操作系統(tǒng)申請(qǐng)?大塊內(nèi)存當(dāng)作對(duì)象堆。之后當(dāng)應(yīng)用程序創(chuàng)建對(duì)象

時(shí),虛擬機(jī)就會(huì)在堆上分配合適的內(nèi)存塊。而當(dāng)對(duì)象不再使用時(shí),虛擬機(jī)就會(huì)將它占用的內(nèi)

存塊歸還給堆。Dalvik虛擬機(jī)也不例外,本文就分析它的Java堆創(chuàng)建過(guò)程。

從前面一文可以知道,在Dalvik虛擬機(jī)中,Java堆實(shí)際上是由一個(gè)Active堆和一個(gè)Zygote

堆組成的,如圖1所示:

HeapSource

Actr>?HeapZygoteHeap

UrdTabte

jveHeapBitmap

MarkHeapBftm^p

圖1Dalvi噓擬機(jī)的Java堆

其中,Zygote堆用來(lái)管理Zygote進(jìn)程在啟動(dòng)過(guò)程中預(yù)加我和創(chuàng)建的各種對(duì)象,而Active堆

是在Zygote進(jìn)程fork第一個(gè)子進(jìn)程之前創(chuàng)建的。之后無(wú)論是Zygoie進(jìn)程還是其子進(jìn)程,都

在Active堆上進(jìn)行對(duì)象分配和釋放。這樣做的H的是使得Zygote進(jìn)程和其子進(jìn)程最大限度

地共享Zygote堆所占用的內(nèi)存。

為了管理Java堆,Dalvik虛擬機(jī)需要一些輔助數(shù)據(jù)結(jié)構(gòu),包括一個(gè)CardTable、兩

個(gè)HeapBitm叩和一個(gè)MarkSlack。CardTable是為了記錄在垃圾收集過(guò)程中對(duì)象的引用情

況的,以便可以實(shí)現(xiàn)ConcurrentGo圖1的兩個(gè)HeapBitmap,一個(gè)稱為L(zhǎng)iveHeapBitmap,

用來(lái)記錄上次GC之后,還存活的對(duì)象,另一個(gè)稱為MarkHeapBitm叩,用來(lái)記錄當(dāng)前GC

中還存活的對(duì)象。這樣,上次GC后存活的但是當(dāng)前GC不存活的對(duì)象,就是需要釋放的對(duì)

象。Davlk虛擬機(jī)使用標(biāo)記-清除(Mark-Sweep)算法進(jìn)行GC。在標(biāo)記階段,通過(guò)一個(gè)Mark

Stack來(lái)實(shí)現(xiàn)遞歸檢查被引用的對(duì)象,即在當(dāng)前GC中存活的對(duì)象。有了這個(gè)MarkStack,

就可以通過(guò)循環(huán)來(lái)模擬函數(shù)遞歸調(diào)用。

Dalvik虛擬機(jī)Java堆的創(chuàng)建過(guò)程實(shí)際上就是上面分析的各種數(shù)據(jù)結(jié)構(gòu)的創(chuàng)建過(guò)程,

它們是在Dalvik虛擬機(jī)啟動(dòng)的過(guò)程中創(chuàng)建的。接下來(lái),我們就詳細(xì)分析這個(gè)過(guò)程。

從前面一文可以知道,Dalvik虛擬機(jī)在啟動(dòng)的過(guò)程中,會(huì)通過(guò)調(diào)用函數(shù)dvmGcSiarlup

來(lái)創(chuàng)建Java堆,它的實(shí)現(xiàn)如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

booldvmGcStartupO

(

dvmInitMutex(&gDvni.gcHeapLock);

pthread_cond_init(&gDvm.gcHeapCond,NULL);

returndvmHeapStartupO;

)

這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Alloc.cpp中。

函數(shù)dvmGcStartup首先是分別初始化一個(gè)鎖和一個(gè)條件變量,它們都是用來(lái)保護(hù)堆

的并行訪問(wèn)的,接著再調(diào)用另夕I、一個(gè)函數(shù)dvmHeapStartup來(lái)創(chuàng)建Java堆。

函數(shù)dvmHeapStartup的實(shí)現(xiàn)如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

booldvmHeapStartupO

(

GcIIeap*gclleap;

if(gDvm.heapGrowthLimil==0){

gDvm.heapGrowthLimit=gDvm.heapMaximumSize;

)

gcHcap=dvniHcapSourccStariup(gDvm.hcapStartingSize,

gDvm.heapMaximumSize,

gDvm.heapGrowthLimit);

gDvm.gcHeap=gcHcap;

if(!dvmCardTableStanup(gDvm.heapMaximumSize,gDvm.heapGrowthLimit)){

LOGE_HEAP("cardtablestartupfailed.");

returnfalse;

}

returntrue;

I

這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Heap.cpp中。

gDvm是一個(gè)類型為DvmGlobals的全局變量,它通過(guò)各個(gè)成員變量記錄了Dalvik

虛擬機(jī)的各種信息。這里涉及到三個(gè)重要與Java堆相關(guān)的信息,分別是Java堆的起始大小

(StartingSize)^最大值(MaximumSize)和增長(zhǎng)上限值(GrowthLimit)?在啟動(dòng)Dalvik

虛擬機(jī)的時(shí)候,我們可以分別通過(guò)-Xms、-Xmx和-XX:HeapGrowthLimil三個(gè)選項(xiàng)來(lái)指定上

述三個(gè)值。

Java堆的起始大小(StartingSize)指定了Davlik虛擬機(jī)在啟動(dòng)的時(shí)候向系統(tǒng)申請(qǐng)的

物理內(nèi)存的大小。后面再根據(jù)需要逐漸向系統(tǒng)申請(qǐng)更多的物理內(nèi)存,直到達(dá)到最大值

(MaximumSize)為止。這是一種按需要分配策略,可以避免內(nèi)存浪費(fèi)。在默認(rèn)情況下,Java

堆的起始大小(StartingSize)和最大值(MaximumSize)等于4M和16M。但是廠商會(huì)通

過(guò)dalvik.vm.heapstartsize和dalvik.vm.heapsize這兩個(gè)屬性將它們?cè)O(shè)置為合適設(shè)備的值的。

注意,雖然Java堆使用的物理內(nèi)存是按需要分配的,但是它使用的虛擬內(nèi)存的總大

小卻是需要在Dalvik啟動(dòng)的時(shí)候就確定的。這個(gè)虛擬內(nèi)存的大小就等于Java堆的最大值

(MaximumSize)o想象一下,如果不這樣做的話,會(huì)出現(xiàn)什么情況。假設(shè)開(kāi)始時(shí)創(chuàng)建的虛

擬內(nèi)存小于Java堆的最大值(MaximumSize),由于實(shí)際情況是允許虛擬內(nèi)存的大小是達(dá)到

Java堆的最大值(MaximumSize)的,因此,當(dāng)開(kāi)始時(shí)創(chuàng)建的虛擬內(nèi)存無(wú)法滿足需求時(shí),

那么就需要重新創(chuàng)建另外一塊更大的虛擬內(nèi)存。這樣就需要將之前的虛擬內(nèi)存的內(nèi)容挑貝到

新創(chuàng)建的更大的虛擬內(nèi)存去,并且還要相應(yīng)地修改各種輔助數(shù)據(jù)結(jié)構(gòu)。這樣太麻煩了,而且

效率也太低了。因此就在一開(kāi)始的時(shí)候,就創(chuàng)建一塊與Java堆的最大值(MaximumSize)

相等的虛擬內(nèi)存。

但是,Dalvik虛擬機(jī)乂希望能夠動(dòng)態(tài)地調(diào)整Java堆的可用最大值,于是就出現(xiàn)了一

個(gè)稱為增長(zhǎng)上限的值(GrowthLimit)o這個(gè)增長(zhǎng)上限值(GrowthLimit),我們可以認(rèn)為它

是Java堆大小的軟限制,而前面所描述的最大值(MaximumSize),是Java堆大小的硬限

制。通過(guò)動(dòng)態(tài)地調(diào)整增長(zhǎng)上限值(GrowthLimit),就可以實(shí)現(xiàn)動(dòng)態(tài)調(diào)整Java堆的可用最大

值,但是這個(gè)增長(zhǎng)上限值必須要小于等于最大值(MaximumSize)o從函數(shù)dvmHeapSiartup

的實(shí)現(xiàn)可以知道,如果沒(méi)有指定Java堆的增長(zhǎng)上限的值(GrowthLimit),那么它的值就等

于Java堆的最大值(MaximumSize)。

事實(shí)上,在全局變量gDvm中,除了上面提到的三個(gè)信息之外,還有三種信息是與

Java堆相關(guān)的,它們分別是堆最小空閑值(MinFree)、堆最大空閑值(MaxFree)和堆目

標(biāo)利用率(TargetUtilization)(>這三個(gè)值可以分別通過(guò)Dalvik虛擬機(jī)的啟動(dòng)選項(xiàng)

-XX:HeapMinFree、-XX:HcapMaxFree和-XX:HeapTargetUtilizalion來(lái)指定。它們用來(lái)確保每

次GC之后,Java堆已經(jīng)使用和空閑的內(nèi)存有一個(gè)合適的比例,這樣可以盡量地減少GC的

次數(shù)。舉個(gè)例子說(shuō),堆的利用率為U,最小空閑值為MinFree字節(jié),最大空閑值為MaxFree

字節(jié)。假設(shè)在某一次GC之后,存活對(duì)象占用內(nèi)存的大小為L(zhǎng)iveSize。那么這時(shí)候堆的理想

大小應(yīng)該為(LivcSize/U)。但是(LivcSize/U)必須大于等于(LivcSize+MinFree)并且小于等

于(LiveSize+MaxFree)?

了解了這些與Java堆大小相關(guān)的信息之后,我們回到函數(shù)dvmGcStartup中,可以

清楚看到,它先是調(diào)用函數(shù)dvniHeapSourceStaitup來(lái)創(chuàng)建一個(gè)Java堆,接著再調(diào)用函數(shù)

dvmCardTableStartup來(lái)為該Java堆創(chuàng)建一個(gè)CardTable。接下來(lái)我們先分析函數(shù)

dvmHeapSourceStarlup的實(shí)現(xiàn),接著再分析函數(shù)dvmCardTableStartup的實(shí)現(xiàn)。

函數(shù)dvmHeapSourceStartup的實(shí)現(xiàn)如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

GcHeap*dvmHeapSourceStartup(size_tstartSize,size_tmaximumSize,

size_tgrowthLimit)

GcHcap*gcHcap;

HeapSource*hs;

mspacemsp;

size_llength;

void*base;

*Allocateacontiguousregionofvirtualmemorytosubdivided

*amongtheheapsmanagedbythegarbagecollector.

*/

length=ALIGN_UP_TO_PAGE_SIZE(maximumSize);

base=dvmAllocRcgion(lengih,PROT_NONE,gDvm.zygote?"dalvik-zygote"

"dalvik-heap");

/*Createanunlockeddlmallocmspacctouseas

*aheapsource.

*/

msp=createMspace(base,klnitialMorecoreStart,startSize);

gcHeap=(GcHeap*)calloc(l,sizeof(*gcHeap));

hs=(HeapSource*)calloc(l,sizeof(*hs));

hs->targetUtilization=gDvm.heapTargetUtilization*HEAP_UTILIZATION_MAX;

hs->minFree=gDvm.heapMinFree;

hs->maxFrcc=gDvm.hcapMaxFrec;

hs->startSize=startSize;

hs->maximumSize=niaximuniSize;

hs->growthLimit=growthLimit;

hs->numHeaps=0;

hs->hcapBasc=(char*)basc;

hs->heapLength=length;

if(!addInitialHeap(hs,msp,growthLimit)){

if(!dvmHeapBitmapInit(&hs->liveBits,base,length,"dalvik-bitmap-r')){

if(!dvmHeapBi(mapInit(&hs->markBits,base,length,'dalvik-bitmap-2")){

if(!allocMarkStack(&gcHeap->>markContext.stack,hs-^maximumSize)){

gcHeap->markContext.bitmap=&hs->markBits;

gcHeap->heapSource=hs;

gHs=hs;

returngcHeap;

這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。

函數(shù)dvmHeapSourceStartup的執(zhí)行過(guò)程如下所示:

1.將參數(shù)maximum指定的最大堆大小對(duì)齊到內(nèi)存頁(yè)邊界,得到結(jié)果為length,并

且調(diào)用函數(shù)dvmAllocRegion分配一塊大小等于length的匿名共享內(nèi)存塊,起始地址為base。

這塊匿名共享內(nèi)存即作為Dalvik虛擬機(jī)的Java堆。

2.調(diào)用函數(shù)createMspace將前面得到的匿名共享內(nèi)存塊封裝為一個(gè)mspace,以便

后面可以通過(guò)C庫(kù)得供的mspace_maUoc和rnspace_bulk_free等函數(shù)來(lái)管理Java堆。這個(gè)

mspace的起始大小為Java堆的起始大小,這意味著一開(kāi)始在該mspace上能夠分配的內(nèi)存不

能超過(guò)Java堆的起始大小。不過(guò)后面我們動(dòng)態(tài)地調(diào)整這個(gè)mspace的大小,使得它可以使用

更多的內(nèi)存,但是不能超過(guò)Java堆的最大值。

3,分配一個(gè)GcHeap結(jié)構(gòu)體gcHeap和一個(gè)HeapSource結(jié)構(gòu)體hs,用來(lái)維護(hù)Java

堆的信息,包括Java堆的目標(biāo)利用率、最小空閑值、最大空閑值、起始大小、最大值、增

長(zhǎng)上限值、堆個(gè)數(shù)、起始地址和大小等信信息。

4.調(diào)用函數(shù)addlnitialHeap在前面得到的匿名共享內(nèi)存上創(chuàng)建一個(gè)Active堆。這個(gè)

Active堆的最大值被設(shè)置為Java堆的起始大小。

5.調(diào)用函數(shù)dvmHeapBitmapInit創(chuàng)建和初始化一個(gè)LiveBitmap和一個(gè)Mark

Bitmap,它們?cè)贕C時(shí)會(huì)用得到。

6.調(diào)用函數(shù)allockMarkStack創(chuàng)建和初始化一個(gè)MarkStack,它在GC時(shí)也會(huì)用到。

7.將前面創(chuàng)建和初始化好的MarkBitmap和HeapSource結(jié)構(gòu)體hs保存在前面創(chuàng)建

的GcHcap結(jié)構(gòu)體gcHcap中,并且將該GcHcap結(jié)構(gòu)體gcHcap返回給調(diào)用者。同時(shí),

HeapSource結(jié)構(gòu)體hs也會(huì)保存在全局變量gHs中。

為了更好地對(duì)照?qǐng)D2來(lái)理解函數(shù)dvmHeapSourceSlartup所做的事情,接下來(lái)我們?cè)?/p>

細(xì)分析上述提到的關(guān)鍵函數(shù)dvmAIIocRegion、createMspace、addlnitialHeap%

dvmHeapBitmapInit和allcckMarkStack的實(shí)現(xiàn)。

函數(shù)dvmAIIocRegion的實(shí)現(xiàn)如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

void*dvmAllocRegion(size_tbyteCounl,iniprot,constchar*name){

void*base;

intfd,ret;

bytcCount=ALIGN_UP_TO_PAGE_SlZE(bytcCount);

fd=ashmem_crcatc_rcgion(namc,bytcCount);

if(fd==-1){

returnNULL;

}

base=rnniap(NULL,byteCount,prot,MAP_PRIVATE,fd,0);

ret=close(fd);

if(base==MAP_FAILED){

returnNULL;

)

if(ret==-1){

munniapCbase,byteCount);

returnNULL;

)

returnbase;

}

這個(gè)函數(shù)定義在文件dalvik/vm/Misc.cpp中。

從這里就可以清楚地看出,函數(shù)dvmAIIocRegion所做的事情就是調(diào)用函數(shù)

ashmem_create_region來(lái)創(chuàng)建一塊匿名共享內(nèi)存。關(guān)于Android系統(tǒng)的匿名共享內(nèi)存,可以

參考前面Android系統(tǒng)匿名共享內(nèi)存Ashmem(AnonymousSharedMemory)簡(jiǎn)要介紹和學(xué)

習(xí)計(jì)劃一文。

函數(shù)createMspace的實(shí)現(xiàn)如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

staticmspacecreateMspace(void*begin,size_tmorecoreStart,size_tstartingSize)

//Clearerrnotoallowstrerroronerror.

errno=0;

//Allowaccesstoinitaipagesthatwillholdmspace.

mprotcct(bcgin,morccorcStart,PROT_READ|PROT_WRITE);

//Createmspaceusingourbackingstoragestartingatbeginandwithafootprintof

//morecorcStart.Don'tuseaninternaldlmalloclock.WhenmorccoreStartbytesofmemory

are

//exhaustedmorecorewillbecalled.

mspacenisp=create_mspace_vvith_base(begin,morecoreStart,false/*Iocked*/);

if(msp!=NULL){

//Donotallowmorccorcrequeststosucceedbeyondthestartingsizeoftheheap.

mspace_sel_fooiprin(_limit(msp,star(ingSize);

}else{

ALOGE("creale_^ispace_with_basefailed%s",slrerror(ermo));

)

returnmsp;

}

這個(gè)函數(shù)定義在文件dalvik/vm/aHoc/HcapSourcc.cpp中。

參數(shù)begin指向前面創(chuàng)建的一塊法名共享內(nèi)存的起始地址,也就是Java堆的起始地

址,函數(shù)createMspace通過(guò)C庫(kù)提供的函數(shù)crcatc_mspacc_\vith_basc將該塊匿名共享內(nèi)存

封裝成一個(gè)mspace,并且通過(guò)調(diào)用C庫(kù)提供的函數(shù)mspace_sel_foolprint_limit設(shè)置該mspace

的大小為Java堆的起始大小。

函數(shù)addlnitialHeap的實(shí)現(xiàn)如下所示:

[epp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

staticbooladdInitialHcap(HcapSourcc*hs,mspacemsp,sizs_tmaximumsizc)

(

assert(hs!=NULL);

assert(msp!=NULL);

if(hs->numHeaps!=0){

returnfalse;

)

hs->hcaps[0].msp=msp;

hs->heaps[O].maximunSize=maximumSize;

hs->heaps[0].concurrentStartBytes=SIZE_MAX;

hs->heaps[()].base=hs->heapBase;

hs->heaps[0].limit=hs->heapBase+maximumSize;

hs->heaps[()].brk=hs->heapBase+klnitialMorecoreStart;

hs->nuniHeaps=1;

returntrue;

}

這個(gè)函數(shù)定義在文件daivik/vm/alloc/HeapSource.cpp中。

在分析函數(shù)addlnitialHeap的實(shí)現(xiàn)之前,我們先解釋一下兩個(gè)數(shù)據(jù)結(jié)構(gòu)HeapSource

和He叩。

在結(jié)構(gòu)體HeapSource中,有一個(gè)類型為Heap的數(shù)組he叩s,如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

structHeapSource{

/*Theheaps;heapsl()jisalwaystheactiveheap,

*whichnewobjectsshouldbeallocatedfrom.

*/

Heapheaps[HEAP_SOURCE_MAX_HEAP_COUNT];

/*Thecurrentnumberofheaps.

*/

size_tnuniHcaps;

};

這個(gè)結(jié)構(gòu)體定義在文件dalvik/vm/alloc/HcapSourcc.cpp中。

這個(gè)Heap數(shù)組最多有HEAP_SOURCE_MAX_HEAP_COUNT個(gè)Heap,并且當(dāng)前擁

有的Heap個(gè)數(shù)記錄在nunipHeaps中。

HEAP_SOURCE_MAX_HEAP_COUNT是一個(gè)宏,定義為2,如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

/*Thelargestnumberofseparateheapswccanhandle.

#defineHEAP_SOURCE_MAX_HEAP_COUNT2

這個(gè)宏定義在文彳匕dalvik/vm/alloc/HeapSource.h中。

這意味著Dalvik虛擬機(jī)的Java堆最多可以劃分為兩個(gè)Heap,就是圖1所示的Active

堆和Zygote堆。

結(jié)構(gòu)Heap的定義如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

structHeap(

/*Themspacetoallocatefrom.

*/

mspacemsp;

/*Thelargestsizethatthisheapisallowedtogrowto.

*/

size_tmaximumSize;

/*Numberofbytesallocatedfromthismspaceforobjects,

*includinganyoverhead.ThisvalueisNOTexact,and

*shouldonlybeusedasaninputforcertainheuristics.

*/

size_tbytesAllocated;

/*Numberofbytesallocatedfromthismspaceatwhicha

*concurrentgarbagecollectionwillbestarted.

*/

sizc_tconcurrcntStartBytes;

/*Numberofobjectscurrentlyallocatedfromthismspace.

*/

size_tobjectsAllocated;

/*

*Thelowestaddressofthisheap,inclusive.

*/

char*basc;

/*

*Thehighestaddressofthisheap,exclusive.

*/

char

/*

*Iftheheaphasanmspace,thecurrenthighwatermarkin

*allocationsrequestedviadvmHeapSourceMorecore.

*/

char*brk;

);

這個(gè)結(jié)構(gòu)體定義在文件dalvik/vm/alloc/HeapSource.cpp中。

結(jié)構(gòu)體Heap用來(lái)描述一個(gè)堆,它的各個(gè)成員變量的含義如下所示:

msp:描述堆所使用內(nèi)存塊。

maximumSize:描述堆可以使用的最大內(nèi)存值。

bytesAllocated:描述堆已經(jīng)分配的字節(jié)數(shù)。

concurrcntStartBytcs:描述堆已經(jīng)分配的內(nèi)存ii到指定值就要觸發(fā)并行GC。

objectsAllocated:描述已經(jīng)分配的對(duì)象數(shù)。

base:描述堆所使用的內(nèi)存塊的起始地址。

limit:描述堆所使用的內(nèi)存塊的結(jié)束地址。

brk:描述當(dāng)前堆所分配的最大內(nèi)存值。

回到函數(shù)addlnitialHeap中,參數(shù)hs和msp指向的是在函數(shù)dvniHeapSourceSlartup

中創(chuàng)建的HeapSource結(jié)構(gòu)體和mspace內(nèi)存對(duì)象,而參數(shù)maximumSize描述的Java堆的增

長(zhǎng)上限值。

通過(guò)函數(shù)addlnitialHeap的實(shí)現(xiàn)就可以看出,Dalvik虛擬機(jī)在啟動(dòng)的時(shí)候,實(shí)際上

只創(chuàng)建了一個(gè)Heap。這個(gè)Heap就是我們?cè)趫D1中所說(shuō)的Active堆,它開(kāi)始的時(shí)候管理的

是整個(gè)Java堆。但是在圖1中,我們說(shuō)Java堆實(shí)際上還包含有一個(gè)Zygote堆的,那么這個(gè)

Zygote堆是怎么來(lái)的呢?

從前面一文可以知道,Zygote進(jìn)程會(huì)通過(guò)調(diào)用函數(shù)forkAndSpecializeCommcn來(lái)

fork子進(jìn)程,其中與Dalvik虛擬機(jī)Java堆相關(guān)的邏輯如下所示:

[cpplviewplaincopy在CODE上查看代碼片派生到我的代碼片

staticpid_tforkAndSpccial:zcCommon(constu4*args,boolisSystcmScrvcr)

(

pid_tpid;

if(!dvmGcPrcZygotcFork()){

pid=fork();

if(pid==0){

}else{

)

returnpid;

I

這個(gè)函數(shù)定義在文件dalvik/vm/native/dalvik_system_Zygote.cpp中。

從這里就可以看出,Zygote進(jìn)程在fork子進(jìn)程之前,會(huì)調(diào)用函數(shù)

dvmGcPreZygoteFork來(lái)處理一下Dalvik虛擬機(jī)Java堆。接下來(lái)我們就看看函數(shù)

dvmGcPreZygoteFork都做了哪些事情。

函數(shù)dvmGcPreZygoteFork的實(shí)現(xiàn)如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

booldvniGcPrcZygoteFork()

(

returndvmHcapSourceStartupBcforcFork();

I

這個(gè)函數(shù)定義在文件dalvik/vm/alloc/Alloc.cpp中。

函數(shù)dvmGcPreZygoteFork只是簡(jiǎn)單地封裝了對(duì)另外一個(gè)函數(shù)

dvmHeapSourceStartupBeforeFork的調(diào)用,后者的實(shí)現(xiàn)如下所示:

fcpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

booldvmHeapSourceStartupBeforeForkO

(

HeapSource*hs=gHs;//usealocaltoavoidtheimplicit"volatile"

HS_BOILERPLATE();

assert(gDvm.zygote);

if(JgDvm.newZygoteHeapAllocated){

/*Ensureheapsaretrimmedtominimizefootprintpre-fork.

?/

trimHeapsO;

/*Createanewheapforpost-forkzygoteallocations.Wconly

*tryonce,evenifitfails.

*/

ALOGV("Splitlingoulnewzygoteheap");

gDvm.newZygoteHeapAllocated=true;

returnaddNewHeap(hs);

)

returntrue;

}

這個(gè)函數(shù)定義在文件daivik/vm/alloc/HcapSource.cpp4'o

前面我們?cè)诜治龊瘮?shù)dvmHeapSourceStartup的實(shí)現(xiàn)時(shí)提到,全局變量gHs指向的

是一個(gè)HeapSource結(jié)構(gòu)體,它描述\Dalvik虛擬機(jī)Java堆的信息。同時(shí),gDvm也是一個(gè)

全局變吊:,它的類型為DvmGlobals。gDvm指向的DvmGlobals結(jié)構(gòu)體的成員變吊:

newZygoteHeapAllocated的值被初始化為false。因此,當(dāng)函數(shù)

dvmHeapSourceStartupBeforeFork第一次被調(diào)用時(shí),它會(huì)先調(diào)用函數(shù)trimHeaps來(lái)將Java堆

中沒(méi)有使用到的內(nèi)存歸還給系統(tǒng),接著再調(diào)用函數(shù)addNewHeap來(lái)創(chuàng)建一個(gè)新的Heap。這

個(gè)新的Heap就是圖1所說(shuō)的Zygote堆了。

由于函數(shù)dvmHeapSourceStartupBeforeFork第一次被調(diào)用之后,gDvm指向的

DvmGlobals結(jié)構(gòu)體的成員變量newZygoteHeapAllocated的值就會(huì)被修改為(rue,因此起到

的效果就是以后Zygote進(jìn)程對(duì)函數(shù)dvmHeapSourceStartupBeforeFork的調(diào)用都是無(wú)用功。這

也意味著Zygote進(jìn)程只會(huì)在fork第一個(gè)子進(jìn)程的時(shí)候,才會(huì)將Java堆劃一分為二來(lái)管理。

接下來(lái)我們就繼續(xù)分析函數(shù)trimHcaps和addNcwHcap的實(shí)現(xiàn),以便更好地理解

Dalvik虛擬機(jī)是如何管理Java堆的。

函數(shù)trimHeaps的實(shí)現(xiàn)如下所示:

[cppjviewplaincopy在CODE上查看代碼片派生到我的代碼片

/*

*Returnunusedmemorytothesystemifpossible.

*/

staticvoidtrimHeaps()

(

HS_BOILERPLATE();

HeapSource*hs=gHs;

sizc_theapBytes=0;

for(sizc_ti=0;i<hs->numlleaps;i++){

Heap*heap=&hs->heaps[i];

/*Returnthewildernesschunktothesystem.*/

inspace_trirn(heap->msp,0);

/*Returnanywholefreepagestothesystem.*/

mspace_inspect_all(heap->msp,releasePagesInRange,&heapBytes);

)

/*Sameforthenativeheap.*/

dlmalloc_trim(0);

size_tnativeBytes=0;

dimalloc_inspect_all(releascPagesInRange,&nativeBytes);

LOGD_HEAP("madviscd%zd(GC)+%zd(native)=%zdtotalbytes",

heapBytes,nativeBytes,heapBytes+nativeBytes);

)

這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。

函數(shù)trimHeaps對(duì)Dalvik虛擬機(jī)使用的Java堆和默認(rèn)Native堆做了同樣的兩件事

情。

第一件事情是調(diào)用C庫(kù)提供的函數(shù)mspacc_trim/d1malloc_trim來(lái)將沒(méi)有使用到的虛

擬內(nèi)存和物理內(nèi)存歸還給系統(tǒng),這是通過(guò)系統(tǒng)調(diào)用mremap來(lái)實(shí)現(xiàn)的。

第二件事情是調(diào)用C庫(kù)提供的函數(shù)mspace_inspect_all/dlmalloc_inspect_all將不能

使用的內(nèi)存碎片對(duì)應(yīng)的物理內(nèi)存歸還給系統(tǒng),這是通過(guò)系統(tǒng)調(diào)用madvise來(lái)實(shí)現(xiàn)的。注意,

在此種情況下,只能歸還無(wú)用的物理內(nèi)存,而不能歸還無(wú)用的虛擬內(nèi)存。因?yàn)闅w還內(nèi)存碎片

對(duì)應(yīng)的虛擬內(nèi)存會(huì)使得堆為整體虛擬地址不連續(xù)。

函數(shù)addNewHeap的實(shí)現(xiàn)如下所示:

[cpp]viewplaincopy在CODE上杳看代碼片派生到我的代碼片

staticbooladdNewHeap(HeapSource*hs)

(

Heapheap;

asserl(hs!=NULL);

if(hs->numHcaps>=HEAP_SOURCE_MAX_HEAP_COUNT){

returnfalse;

)

mcmsct(&hcap,0,sizcof(hcap));

/*

*Heapstoragecomesfromacommonvirtualmemoryreservation.

*Thenewheapwillstartonthepageaftertheoldheap.

*/

char*base=hs->he叩s[O].brk;

sizc_toverhead=base-hs->hcaps[O].base;

assert(((size_t)hs->heaps[O].base&(SYSTEM_PAGE_SIZE-1))==0);

if(overhead+hs->mirFree>=hs->maximumSize){

returnfalse;

}

size_tmorecoreStart=SYSTEM_PAGE_SIZE;

heap.maximumSize=hs->growthLimit-overhead;

hcap.concurrcntStartBytes=hs->minFrec-CONCURRENT_START;

heap.base=base;

heap.limit=heap,base+heap.maximumSize;

heap.brk=heap.base+morecoreStart;

if(!「emapNewHe叩(hs.&heap)){

returnfalse;

}

hcap.msp=crcatcMspacc(basc,morecoreStart.hs->ininFrcc);

if(heap.msp==NULL){

returnfalse;

/*Don'tletthesoon-tc-be-oldheapgrowanyfurther.

*/

hs->hcaps[O].inaximumSizc=overhead;

hs->heaps[OJ.limit=base;

mspace_set_footprint_liniit(hs->heaps[0].msp,overhead);

/*Putthenewhe叩inthelist,atheaps[0].

*Shiftexistingheapsdown.

*/

memmove(&hs->heaps[1],&hs->heaps[0],hs->numHeaps*sizcof(hs->hcaps[OJ));

hs->heaps[0]=heap;

hs->numHeaps++;

returntrue;

這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapSource.cpp中。

函數(shù)addNcwHcap所做的事情實(shí)際上就是將前面創(chuàng)建的Dalvik虛擬機(jī)Java堆一分

為二,得到兩個(gè)Heap。

在劃分之前,HeadSource結(jié)構(gòu)體hs只有一個(gè)Heap,如圖2所示:

overhwd

ta->heap$lo).basehs->heaps(0]brkhs->heap$[0].Wnrt

圖2Dalvi噓擬機(jī)Java堆一分為二之前

接下來(lái)在未使用的Dalvi噓擬機(jī)Java堆中創(chuàng)建另外一個(gè)Heap,如圖3所示:

h$->hejps[O].t>asehs->hups[0].trkhwnMheap.bm?t

圖3在未使用的Dalvi噓擬機(jī)Java地中創(chuàng)建一個(gè)新的Heap

最后調(diào)整HeadSource結(jié)構(gòu)體hs的heaps數(shù)組,即交heaps網(wǎng)和heaps⑴的值,結(jié)果

如圖4所示:

hs->hea3s[l].tanrths->heap5(O].ba$e

overhead

h5->heaps⑴.basehs->he?pS[l].br1chS->heap$[O].brkhs->he?p5|0pimrt

圖4DaM噓擬機(jī)Java堆一分為二之后

其中,heaps[l]就是我們?cè)趫D?中所說(shuō)的Zygote堆,而heaps⑼就是我們?cè)趫D1中所說(shuō)的Active

堆。以后無(wú)論是Zygote進(jìn)程,還是Zygote子進(jìn)程,需要分配對(duì)象時(shí),都在Active堆上進(jìn)行。

這樣就可以使得Zygote城最大限度地在Zygote進(jìn)程及其子進(jìn)程中共享。

這樣我們就分析完了函數(shù)addlnitialHcap及其相關(guān)函數(shù)的實(shí)現(xiàn),接下來(lái)我們繼續(xù)分析

函數(shù)dvmHeapBitmapInil和allocMarkSlack的實(shí)現(xiàn)。

函數(shù)dvniHeapBicmapInit的實(shí)現(xiàn)如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

booldvmHeapBitmapInil(HeapBilmap*hb,constvoid*base,size」maxSize,

constchar*name)

(

void*bits;

size_tbitsLen;

asscrt(hb!=NULL);

asscrt(name!=NULL);

bitsLcn=HB_OFFSET_TO」NDEX(maxSize)*sizcof(*hb->bits);

bits=dvmAllocRegion(bilsLen,PROT_READ|PROT_WRITE,name);

if(bits==NULL){

ALOGE("Couldnotnunap%zd-byteashmemregion'%s"\bitsLen,name);

returnfalse;

)

hb->bits=(unsignedlong*)bits;

hb->bitsLcn=hb->allocLcn=bitsLcn;

hb->base=(uintptr_t)base;

hb->max=hb->base-1;

returntrue;

)

這個(gè)函數(shù)定義在文件dalvik/vm/alloc/HeapBitmap.cpp中。

參數(shù)hb指向一個(gè)HcapBitmap結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體正是函數(shù)dvmHcapBitmapInit要

進(jìn)行初始化的。參數(shù)base和maxSize描述的是Java堆的起始地址和大小。另外一個(gè)參數(shù)name

描述的是參數(shù)hb指向的HeapBitm叩結(jié)構(gòu)體的名稱。

在分析函數(shù)dvmHeapBitmapInit的實(shí)現(xiàn)之前,我們先來(lái)了解一下結(jié)構(gòu)體HeapBitmap

的定義,如下所示:

[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

structHeapBmap{

/*Thebitmapdata,whichpointstoanmniap()edareacfzeroed

*anonymousmemory.

*/

unsignedlong*bits;

/*Thesizeoftheusedmemorypointedtobybits,inbytes.This

*valuechangeswhenthebitmapisshrunk.

*/

size_tbitsLen;

/*Therealsizeofthememorypointedtobybits.Thisisthe

*numberofbyteswerequestedfromtheallocatoranddoesnot

*change.

*/

size_tallocLcn;

/*Thebaseaddress,whichcorrespondstothefirstbitin

*thebitmap.

*/

uintptr_tbase;

/*Thehighestpointervalueeverreturnedbyanallocation

*from(hisheap.I.e.,(hehighestaddressthatmaycorrespond

*toasetbit.Iftherearenobitsset,(max<base).

*/

uintptr_tmax;

);

這個(gè)結(jié)構(gòu)體定義在文件dalvik/vm/alloc/HeapBilir.ap.h。

代碼對(duì)HcapBitm叩結(jié)構(gòu)體的各個(gè)成員變量的含義已經(jīng)有很詳細(xì)的注釋,其中最重

要的就是成員變量bits指向的一個(gè)類型為unsignedlong的數(shù)組,這個(gè)數(shù)組的每一個(gè)bit都用

來(lái)標(biāo)記一個(gè)對(duì)象是否存活,

回到函數(shù)dvmHeapBitmapInit中,Java堆的起始地址為base,大小為maxSize,由此

我們就知道,在Java堆上創(chuàng)建的對(duì)象的地址范圍為[base,maxSize)。但是通過(guò)C庫(kù)提供的

mspace_malloc來(lái)在Java堆分配內(nèi)存時(shí),得到的內(nèi)存地虻是以8字節(jié)對(duì)齊的。這意味著我們

只需要(maxSize/8)個(gè)bit來(lái)描述Java堆的對(duì)象。結(jié)構(gòu)體HcapBitmap的成員變量bits是一個(gè)

類型為unsignedlong的數(shù)組,也就是說(shuō),數(shù)組中的每一個(gè)元素都可以描述sizeof(unsignedlong)

個(gè)對(duì)象的存活。在32位設(shè)備上,一個(gè)unsignedlong占用32個(gè)bit,這意味著需要一個(gè)大小

為(maxSize/8/32)的unsignedlong數(shù)組來(lái)描述Java堆對(duì)象的存活。如果換成字節(jié)數(shù)來(lái)描述

的話,就是說(shuō)我們需要一塊大小為(maxSize/8/32)X4的內(nèi)存塊來(lái)描述一個(gè)大小為

maxSize的Java堆對(duì)象。

Dalvik虛擬機(jī)提供了一些宏來(lái)描述對(duì)象地址與HcapBitmap結(jié)構(gòu)體的成員變量bits所

描述的unsignedlong數(shù)組的關(guān)系,如下所示:

[epp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

#defineHB_OBJECT_ALIGNMENT8

#defineHB_BITS_PER_WORD(sizeof(unsignedlong)*CHAR_BIT)

/*<offsct>isthedifferencefrom.basetoapointeraddress.

*<index>istheindexof.bitsthatcontainsthebitrepresenting

*<offset>.

*/

#defineHB_OFFSET_TOJNDEX(offset_)\

((uintptr_t)(offset_)/HB_OBJECT_AL1GNMENT/HB_BITS_PER_WORD)

#defineHB_INDEX_TO_OFFSET(index.)\

((uintptr_t)(index_)*HB_OBJECT_ALIGNMENT*HB_BITS_PER_WORD)

#defineHB_OFFSET_TO_BYTE」NDEX(offset_)\

(HB_OFFSET_TO_INDEX(offset_)*sizeof(*((HeapBitmap*)0)->bils))

這些宏定義在文巳dalvik/vm/alloc/HeapBitmap.h中。

假設(shè)我們知道了一個(gè)對(duì)象的地址為ptr,Java堆的起始地址為base,那么就可以計(jì)算

得到一個(gè)偏移值offset。有了這個(gè)偏移值之后,就可以通過(guò)宏HB_OFFSET_TO」NDEX計(jì)算

得到用來(lái)描述該對(duì)象存活的bit位于HcapBitmap結(jié)構(gòu)體的成員變量bits所描述的unsigned

long數(shù)組的索引index。有了這個(gè)index之后,我們就可以得到一個(gè)unsignedlong值。接著

再通過(guò)對(duì)象地址ptr的第4到第8位表示的數(shù)值為索引,在前面找到的unsignedlong值取出

相應(yīng)的位,就可以得到該對(duì)象是否存活了。

相反,給出一個(gè)HeapBitmap結(jié)構(gòu)體的成員變顯bits所描述的unsignedlong數(shù)組的

索引index,我們可以通過(guò)宏HB」NDEX_TO_OFFSET找到一個(gè)偏移值offset,將這個(gè)偏移

值加上Java堆的起始地址base,就可以得到一個(gè)Java對(duì)象的地址ptr。

第三個(gè)宏HB_OFFSET_TO_BYTEJNDEX借助宏HB_OFFSET_TO_INDEX來(lái)找出

用來(lái)描述對(duì)象存活的bitHeapBitmap結(jié)構(gòu)體的成員變量bits所描述的內(nèi)存塊的字節(jié)索弓I。

有了上述的基礎(chǔ)知識(shí)之后,函數(shù)dvmHcapBitinapInit的實(shí)現(xiàn)就一目了然了。

接下來(lái)我們?cè)賮?lái)看函數(shù)allocMarkStack的實(shí)現(xiàn),如下所示:

[epp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

staticboolallocMarkStack(GcMarkS(ack*s(ack,size_tmaximumSize)

(

constchar*name="dalvik-mark-stack";

void*addr;

assert(stack!=NULL);

stack->lcngth=maximumSize*sizcof(Objcct*)/

(sizeof(Object)+HEAP_SOURCE_CHUNK_OVERHEAD);

addr=dvmAllocRegicn(stack->length,PROT_READ|PROT_WRITE,name);

if(addr==NULL){

returnfalse;

stack->base=(constObject**)addr;

stack->limit=(constObject**)((char*)addr+stack->length);

stack->top=NULL;

niadvise(stack->base,stack->length,MADV_DONTNEED);

returntrue;

)

這個(gè)函數(shù)定義在文件vm/alloc/HeapSource.cpp中。

參數(shù)stack指向的是一個(gè)GcMarkStack結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體正是函數(shù)allocMarkStack

要進(jìn)行初始化的。參數(shù)maximumSize描述的是Java堆的大小。

同樣是在分析函數(shù)allocMarkStack的實(shí)現(xiàn)之前,我們先來(lái)了解一下結(jié)構(gòu)體

GcMarkStack的定義,如下所示:

[epp]viewplaincopy在CODE上查看代碼片派生到我的代碼片

structGcMarkStack{

/*Highestaddress(exclusive)

*/

constObject

/*Currenttopofthestack(exclusive)

*/

constObject**top;

/*Lowestaddress(inclusive)

*/

constObject**base;

/*Maximumstacksize,inbytes.

*/

size_tlength;

};

這個(gè)結(jié)構(gòu)體定義在文件dalvik/vm/alloc/MarkSwecp.h<>

代碼對(duì)HeapBitmap結(jié)構(gòu)體的各個(gè)成員變量的含義己經(jīng)有很詳細(xì)的注釋??偨Y(jié)來(lái)說(shuō),

GcMarkStack通過(guò)一個(gè)Object*數(shù)組來(lái)描述一個(gè)棧。這人Object*數(shù)組的大小通過(guò)成員變量

length來(lái)描述。成員變量base和limit分別描述棧的最低地址和最高地址,另外一個(gè)成員變

量top指向棧頂。

回到函數(shù)allocMarkStack中,我們分析一下需要一個(gè)多大的棧來(lái)描述Java堆的所有

對(duì)象。首先,每一個(gè)Java對(duì)象都是必須要從Object結(jié)構(gòu)體繼承卜來(lái)的,這意味著每一個(gè)Java

對(duì)象占用的內(nèi)存都至少為sizeof(Object)。其次,通過(guò)C庫(kù)提供的接口mspace_mallocJava

堆上為對(duì)象分配內(nèi)存時(shí),C庫(kù)自己需要?些額外的內(nèi)存來(lái)管理該塊內(nèi)存,例如用額外的4個(gè)

字節(jié)來(lái)記錄分配出去的內(nèi)存塊的大小。額外需要的內(nèi)存大小通過(guò)宏

HEAP_SOURCE_CHUNK_OVERHEAD來(lái)描述。最后,我們就可以知道,一個(gè)大小為

maximumSize的Java堆,在最壞情況下,存在(maximumSize/(sizeof(Object)+

HEAP_SOURCE_CHUNK_OVERHEAD))個(gè)對(duì)象。也就是說(shuō),GcMarkStack通過(guò)一個(gè)大小為

(maximumSize/(sizcof(Objcct)+HEAP_SOURCE_CHUNK_OVERHEAD))WObject*數(shù)組來(lái)

描述一個(gè)棧。如果換成字節(jié)數(shù)來(lái)描述的話,就是說(shuō)我們需要一塊大小為(maximumSize*

sizeof(Object*)/(sizeof(Object)+HEAP_SOURCE_CHLNK_OVERHEAD))的內(nèi)存塊來(lái)描述

一個(gè)GcMarkStack棧。

有了上述的基礎(chǔ)知識(shí)之后,函數(shù)allocMarkStack的實(shí)現(xiàn)同樣也一目了然了。

這樣,函數(shù)dvmHcapSourccStartup及其相關(guān)的函數(shù)dvmAilocRcgion>crcatcMspace>

addInitialHeap>dvmHeapBiimapInil和allockMarkSlack的實(shí)現(xiàn)我們就分析完了,回到前面的

函數(shù)dvmHeapStailup中,它調(diào)用函數(shù)dvmHeapSourceStartup創(chuàng)建完成Java堆及其相關(guān)的

HeapBitmap和MarkSlack之后,還需要繼續(xù)調(diào)用函數(shù)dvmCardTableStartup來(lái)創(chuàng)建

溫馨提示

  • 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)論