ART運(yùn)行時(shí)和Dalvik虛擬機(jī)1樣,在堆上為對(duì)象分配內(nèi)存時(shí)都要解決內(nèi)存碎片和內(nèi)存不足問(wèn)題。內(nèi)存碎片問(wèn)題可使用dlmalloc技術(shù)解決。內(nèi)存不足問(wèn)題則通過(guò)垃圾回收和在允許范圍內(nèi)增長(zhǎng)堆大小解決。由于垃圾回收會(huì)影響程序,因此ART運(yùn)行時(shí)采取力度從小到大的進(jìn)垃圾回收策略。1旦力度小的垃圾回收履行過(guò)后能滿(mǎn)足分配要求,那就不需要進(jìn)行力度大的垃圾回收了。本文就詳細(xì)分析ART運(yùn)行時(shí)在堆上為對(duì)象分配內(nèi)存的進(jìn)程。
本博參加博客之星評(píng)選,求投票:點(diǎn)擊投票
老羅的新浪微博:http://weibo.com/shengyangluo,歡迎關(guān)注!
從前面ART運(yùn)行時(shí)Java堆創(chuàng)建進(jìn)程分析1文可以知道,在ART運(yùn)行時(shí)中,主要用來(lái)分配對(duì)象的堆空間Zygote Space和Allocation Space的底層使用的都是匿名同享內(nèi)存,并且通過(guò)C庫(kù)提供的malloc和free接口來(lái)分進(jìn)行管理。這樣就能夠通過(guò)dlmalloc技術(shù)來(lái)盡可能解決碎片問(wèn)題。這1點(diǎn)與我們?cè)谇懊鍰alvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程分析1文提到的Dalvik虛擬機(jī)解決堆內(nèi)存碎片問(wèn)題的方法是1樣的。因此,接下來(lái)在分析ART運(yùn)行時(shí)為新創(chuàng)建對(duì)象分配的進(jìn)程中,主要會(huì)分析它是如何解決內(nèi)存不足的問(wèn)題的。
ART運(yùn)行時(shí)為新創(chuàng)建對(duì)象分配的進(jìn)程如圖1所示:
圖1 ART運(yùn)行時(shí)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程
對(duì)照Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程分析1文的圖2,可以發(fā)現(xiàn),ART運(yùn)行時(shí)和Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程幾近是1模1樣的,它們的區(qū)分僅僅是在于垃圾搜集的方式和策略不同。
從前面Android運(yùn)行時(shí)ART履行類(lèi)方法的進(jìn)程分析1文可以知道,ART運(yùn)行時(shí)為從DEX字節(jié)碼翻譯得到的Native代碼提供的1個(gè)函數(shù)調(diào)用表中,有1個(gè)pAllocObject接口,是用來(lái)分配對(duì)象的。當(dāng)ART運(yùn)行時(shí)以Quick模式運(yùn)行在ARM體系結(jié)構(gòu)時(shí),上述提到的pAllocObject接口由函數(shù)art_quick_alloc_object來(lái)實(shí)現(xiàn)。因此,接下來(lái)我們就從函數(shù)art_quick_alloc_object的實(shí)現(xiàn)開(kāi)始分析ART運(yùn)行時(shí)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程。
函數(shù)art_quick_alloc_object的實(shí)現(xiàn)以下所示:
這是1段ARM匯編,我們需要注意的1點(diǎn)是Native代碼調(diào)用ART運(yùn)行時(shí)提供的對(duì)象分配接口的參數(shù)傳遞方式。其中,參數(shù)type_idx描寫(xiě)的是要分配的對(duì)象的類(lèi)型,通過(guò)寄存器r0傳遞,參數(shù)method描寫(xiě)的是當(dāng)前調(diào)用的類(lèi)方法,通過(guò)寄存器r1傳遞。
函數(shù)art_quick_alloc_object是通過(guò)調(diào)用另外1個(gè)函數(shù)artAllocObjectFromCode來(lái)分配對(duì)象的。函數(shù)art_quick_alloc_object除傳遞前面描寫(xiě)的參數(shù)type_idx和method給函數(shù)artAllocObjectFromCode以外,還會(huì)傳遞另外的兩個(gè)參數(shù)。其中1個(gè)是描寫(xiě)當(dāng)前線(xiàn)程的1個(gè)Thread對(duì)象,該對(duì)象總是保存在寄存器r9中,現(xiàn)在由于要通過(guò)參數(shù)的情勢(shì)傳遞給另外1個(gè)函數(shù),因此就將它放在寄存器r2。另外1個(gè)是棧指針sp,也是由于要通過(guò)參數(shù)的情勢(shì)的傳遞另外1個(gè)函數(shù),這里也會(huì)將它放在寄存器r3中。
函數(shù)artAllocObjectFromCode的實(shí)現(xiàn)以下所示:
函數(shù)artAllocObjectFromCode又是通過(guò)調(diào)用另外1個(gè)函數(shù)AllocObjectFromCode來(lái)分配對(duì)象的。不過(guò),在調(diào)用函數(shù)AllocObjectFromCode之前,函數(shù)artAllocObjectFromCode會(huì)先調(diào)用另外1個(gè)函數(shù)FinishCalleeSaveFrameSetup在當(dāng)前調(diào)用棧幀中保存1個(gè)運(yùn)行時(shí)信息。這個(gè)運(yùn)行時(shí)信息描寫(xiě)的是接下來(lái)要調(diào)用的方法的類(lèi)型為Runtime::kRefsOnly,也就是由被調(diào)用者保存那些不是用來(lái)傳遞參數(shù)的通用寄存器,即除r0-r3的其它通用寄存器。
函數(shù)AllocObjectFromCode的實(shí)現(xiàn)以下所示:
參數(shù)type_idx描寫(xiě)的是要分配的對(duì)象的類(lèi)型,函數(shù)AllocObjectFromCode需要將它解析為1個(gè)Class對(duì)象,以即可以取得更多的信息進(jìn)行內(nèi)存分配。
函數(shù)AllocObjectFromCode首先是在當(dāng)前調(diào)用類(lèi)方法method的Dex Cache中檢查是不是已存在1個(gè)與參數(shù)type_idx對(duì)應(yīng)的Class對(duì)象。如果已存在,那末就說(shuō)明參數(shù)type_idx描寫(xiě)的對(duì)象類(lèi)型已被加載和解析過(guò)了,因此這時(shí)候候就能夠直接拿來(lái)使用。否則的話(huà),就通過(guò)調(diào)用保存在當(dāng)前運(yùn)行時(shí)對(duì)象內(nèi)部的1個(gè)ClassLinker對(duì)象的成員函數(shù)ResolveType來(lái)對(duì)參數(shù)type_idx描寫(xiě)的對(duì)象類(lèi)型進(jìn)行加載和解析。關(guān)于Dex Cache的知識(shí),可以參數(shù)前面Android運(yùn)行時(shí)ART履行類(lèi)方法的進(jìn)程分析1文,而對(duì)象類(lèi)型(即類(lèi))的加載和解析進(jìn)程可以參考前面Android運(yùn)行時(shí)ART加載類(lèi)和方法的進(jìn)程分析1文。
得到了要分配的對(duì)象的類(lèi)型klass以后,如果參數(shù)access_check的值等于true,那末就對(duì)該類(lèi)型進(jìn)行檢查,即檢查它是不是可以實(shí)例化和是不是可以訪(fǎng)問(wèn)。如果檢查通過(guò),或不需要檢查,那末接下來(lái)還要確保類(lèi)型klass是已初始化過(guò)了的。前面的檢查都沒(méi)有問(wèn)題以后,最后函數(shù)AllocObjectFromCode就調(diào)用Class類(lèi)的成員函數(shù)AllocObject來(lái)分配1個(gè)類(lèi)型為klass的對(duì)象。
Class類(lèi)的成員函數(shù)AllocObject的實(shí)現(xiàn)以下所示:
這里我們就終究看到調(diào)用ART運(yùn)行時(shí)內(nèi)部的Heap對(duì)象的成員函數(shù)AllocObject在堆上分配對(duì)象了,其中,要分配的大小保存在當(dāng)前Class對(duì)象的成員變量object_size_中。
Heap類(lèi)的成員函數(shù)AllocObject的實(shí)現(xiàn)以下所示:
這個(gè)函數(shù)定義在文件art/runtime/gc/heap.cc中。
Heap類(lèi)的成員函數(shù)AllocObject首先是要肯定要在哪一個(gè)Space上分配內(nèi)存??梢苑峙鋬?nèi)存的Space有3個(gè),分別Zygote Space、Allocation Space和Large Space。不過(guò),Zygote Space在還沒(méi)有劃分出Allocation Space之前,就在Zygote Space上分配,而當(dāng)Zygote Space劃分出Allocation Space以后,就只能在Allocation Space上分配。從前面ART運(yùn)行時(shí)Java堆創(chuàng)建進(jìn)程分析1文可以知道,Heap類(lèi)的成員變量alloc_space_在Zygote Space在還沒(méi)有劃分出Allocation Space之前指向Zygote Space,劃分以后就指向Allocation Space。Large Object Space則始終由Heap類(lèi)的成員變量large_object_space_指向。
只要滿(mǎn)足以下3個(gè)條件,就在Large Object Space上分配,否則就在Zygote Space或Allocation Space上分配:
1. 要求分配的內(nèi)存大于等于Heap類(lèi)的成員變量large_object_threshold_指定的值。這個(gè)值等于3 * kPageSize,即3個(gè)頁(yè)面的大小。
2. 已從Zygote Space劃分出Allocation Space,即Heap類(lèi)的成員變量have_zygote_space_的值等于true。
3. 被分配的對(duì)象是1個(gè)原子類(lèi)型數(shù)組,即byte數(shù)組、int數(shù)組和boolean數(shù)組等。
肯定好要在哪一個(gè)Space上分配內(nèi)存以后,就能夠調(diào)用Heap類(lèi)的成員函數(shù)Allocate進(jìn)行分配了。如果分配成功,Heap類(lèi)的成員函數(shù)Allocate就返回新分配的對(duì)象,保存在變量obj中。接下來(lái)再做3件事情:
1. 調(diào)用Object類(lèi)的成員函數(shù)SetClass設(shè)置新分配對(duì)象obj的類(lèi)型。
2. 調(diào)用Heap類(lèi)的成員函數(shù)RecordAllocation記錄當(dāng)前的內(nèi)存分配狀態(tài)。
3. 檢查當(dāng)前已分配出去的內(nèi)存是不是已到達(dá)由Heap類(lèi)的成員變量concurrent_start_bytes_設(shè)定的閥值。如果到達(dá),那末就調(diào)用Heap類(lèi)的成員函數(shù)RequestConcurrentGC通知GC履行1次并行GC。關(guān)于履行并行GC的閥值,接下來(lái)分要ART運(yùn)行時(shí)的垃圾搜集進(jìn)程中再詳細(xì)分析。
另外一方面,如果Heap類(lèi)的成員函數(shù)Allocate分配內(nèi)存失敗,則Heap類(lèi)的成員函數(shù)AllocObject拋出1個(gè)OOM異常。
接下來(lái),我們先分析Heap類(lèi)的成員函數(shù)RecordAllocation的實(shí)現(xiàn),接著再分析Heap類(lèi)的成員函數(shù)Allocate的實(shí)現(xiàn)。由于后者的履行流程比較復(fù)雜,而前者的履行流程比較簡(jiǎn)單。我們先分析容易的,以避免打斷后面的分析。
Heap類(lèi)的成員函數(shù)RecordAllocation的實(shí)現(xiàn)以下所示:
Heap類(lèi)的成員函數(shù)RecordAllocation首先是記錄當(dāng)前已分配的內(nèi)存字節(jié)數(shù)和對(duì)象數(shù),接著再將新分配的對(duì)角壓入到Heap類(lèi)的成員變量allocation_stack_描寫(xiě)的Allocation Stack中去。后面這1點(diǎn)與Dalvik虛擬機(jī)的做法是不1樣的。Dalvik虛擬機(jī)直接將新分配出來(lái)的對(duì)象記錄在Live Bitmap中,具體可以參考前面Dalvik虛擬機(jī)為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程分析1文。ART運(yùn)行時(shí)之所以要將新分配的對(duì)象壓入到Allocation Stack中去,是為了以后可以履行Sticky GC。
注意,如果不能成功將將新分配的對(duì)角壓入到Allocation Stack中,就說(shuō)明上次GC以來(lái),新分配的對(duì)象太多了,因此這時(shí)候候就需要履行1個(gè)Sticky GC,將Allocation Stack里面的垃圾進(jìn)行回收,然后再?lài)L試將新分配的對(duì)象壓入到Allocation Stack中,直到成功為止。
接下來(lái)我們就重點(diǎn)分析Heap類(lèi)的成員函數(shù)Allocate的實(shí)現(xiàn),以即可以了解新創(chuàng)建對(duì)象在堆上分配的具體進(jìn)程,以下所示:
這個(gè)函數(shù)定義在文件art/runtime/gc/heap.cc中。
Heap類(lèi)的成員函數(shù)Allocate首先調(diào)用成員函數(shù)TryToAllocate嘗試在不履行GC的情況下進(jìn)行內(nèi)存分配。如果分配失敗,再調(diào)用成員函數(shù)AllocateInternalWithGc進(jìn)行帶GC的內(nèi)存分配。
Heap類(lèi)的成員函數(shù)Allocate是1個(gè)模板函數(shù),不同類(lèi)型的Space會(huì)致使調(diào)用不同重載的成員函數(shù)TryToAllocate進(jìn)行不帶GC的內(nèi)存分配。雖然可以用來(lái)分配內(nèi)存的Space有Zygote Space、Allocation Space和Large Object Space3個(gè),但是前二者的類(lèi)型是相同的,因此實(shí)際上只有兩個(gè)不同重載版本的成員函數(shù)TryToAllocate,它們的實(shí)現(xiàn)以下所示:
Heap類(lèi)兩個(gè)重載版本的成員函數(shù)TryToAllocate的實(shí)現(xiàn)邏輯都幾近是相同的,首先是調(diào)用另外1個(gè)成員函數(shù)IsOutOfMemoryOnAllocation判斷分配要求的內(nèi)存后是不是會(huì)超過(guò)堆的大小限制。如果超過(guò),則分配失??;否則的話(huà)再在指定的Space進(jìn)行內(nèi)存分配。
Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation的實(shí)現(xiàn)以下所示:
Heap類(lèi)的成員變量num_bytes_allocated_描寫(xiě)的是目前已分配出去的內(nèi)存字節(jié)數(shù),成員變量max_allowed_footprint_描寫(xiě)的是目前堆可分配的最大內(nèi)存字節(jié)數(shù),成員變量growth_limit_描寫(xiě)的是目前堆允許增長(zhǎng)到的最大內(nèi)存字節(jié)數(shù)。這里需要注意的1點(diǎn)是,max_allowed_footprint_是Heap類(lèi)施加的1個(gè)限制,不會(huì)對(duì)各個(gè)Space實(shí)際可分配的最大內(nèi)存字節(jié)數(shù)產(chǎn)生影響,并且各個(gè)Space在創(chuàng)建的時(shí)候,已把自己可分配的最大內(nèi)存數(shù)設(shè)置為允許使用的最大內(nèi)存字節(jié)數(shù)的。
如果目前堆已分配出去的內(nèi)存字節(jié)數(shù)再加上要求分配的內(nèi)存字節(jié)數(shù)new_footprint小于等于目前堆可分配的最大內(nèi)存字節(jié)數(shù)max_allowed_footprint_,那末分配出要求的內(nèi)存字節(jié)數(shù)以后不會(huì)造成OOM,因此Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation就返回false。。
另外一方面,如果目前堆已分配出去的內(nèi)存字節(jié)數(shù)再加上要求分配的內(nèi)存字節(jié)數(shù)new_footprint大于目前堆可分配的最大內(nèi)存字節(jié)數(shù)max_allowed_footprint_,并且也大于目前堆允許增長(zhǎng)到的最大內(nèi)存字節(jié)數(shù)growth_limit_,那末分配出要求的內(nèi)存字節(jié)數(shù)以后造成OOM,因此Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation就返回true。
剩下另外1種情況,目前堆已分配出去的內(nèi)存字節(jié)數(shù)再加上要求分配的內(nèi)存字節(jié)數(shù)new_footprint大于目前堆可分配的最大內(nèi)存字節(jié)數(shù)max_allowed_footprint_,但是小于等于目前堆允許增長(zhǎng)到的最大內(nèi)存字節(jié)數(shù)growth_limit_,這時(shí)候候就要看情況了會(huì)不會(huì)出現(xiàn)OOM了。如果ART運(yùn)行時(shí)運(yùn)行在非并行GC的模式中,即Heap類(lèi)的成員變量concurrent_gc_等于false,那末取決于允不允許增長(zhǎng)堆的大小,即參數(shù)grow的值。如果不允許,那末Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation就返回true,表示當(dāng)前要求的分配會(huì)造成OOM。如果允許,那末Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation就會(huì)修改目前堆可分配的最大內(nèi)存字節(jié)數(shù)max_allowed_footprint_,并且返回false,表示允許當(dāng)前要求的分配。這意味著,在非并行GC運(yùn)行模式中,在分配內(nèi)存進(jìn)程中遇到內(nèi)存不足,并且當(dāng)前可分配內(nèi)存還未到達(dá)增長(zhǎng)上限時(shí),要等到履行完成1次非并行GC后,才能成功分配到內(nèi)存,由于每次履行完成GC以后,都會(huì)依照預(yù)先設(shè)置的堆目標(biāo)利用率來(lái)增長(zhǎng)堆的大小。
另外一方面,如果ART運(yùn)行時(shí)運(yùn)行在并行GC的模式中,那末只要前堆已分配出去的內(nèi)存字節(jié)數(shù)再加上要求分配的內(nèi)存字節(jié)數(shù)new_footprint不地超過(guò)目前堆允許增長(zhǎng)到的最大內(nèi)存字節(jié)數(shù)growth_limit_,那末就不管允不允許增長(zhǎng)堆的大小,都認(rèn)為不會(huì)產(chǎn)生OOM,因此Heap類(lèi)的成員函數(shù)IsOutOfMemoryOnAllocation就返回false。這意味著,在并行GC運(yùn)行模式中,在分配內(nèi)存進(jìn)程中遇到內(nèi)存不足,并且當(dāng)前可分配內(nèi)存還未到達(dá)增長(zhǎng)上限時(shí),不過(guò)等到履行并行GC后,就有可能成功分配到內(nèi)存,由于實(shí)際履行內(nèi)存分配的Space可分配的最大內(nèi)存字節(jié)數(shù)是足夠的。
回到前面Heap類(lèi)的成員函數(shù)TryToAllocate中,從前面ART運(yùn)行時(shí)Java堆創(chuàng)建進(jìn)程分析1文可以知道,對(duì)Large Object Space版本的成員函數(shù)TryToAllocate,調(diào)用的是LargeObjectMapSpace類(lèi)的成員函數(shù)Alloc進(jìn)行內(nèi)存分配,而對(duì)Zygote Space或Allocation Space版本的成員函數(shù)TryToAllocate,如果成員變量running_on_valgrind_的值等于true,就調(diào)用ValgrindDlMallocSpace類(lèi)的成員函數(shù)AllocNonvirtual進(jìn)行內(nèi)存分配,否則就調(diào)用DlMallocSpace類(lèi)的成員函數(shù)Alloc進(jìn)行內(nèi)存分配。我們假定Heap類(lèi)的成員變量running_on_valgrind_的值等于false,因此接下來(lái)我們主要分析LargeObjectMapSpace類(lèi)的成員函數(shù)Alloc和DlMallocSpace類(lèi)的成員函數(shù)Alloc的實(shí)現(xiàn)。
LargeObjectMapSpace類(lèi)的成員函數(shù)Alloc的實(shí)現(xiàn)以下所示:
從這里就能夠看到,Large Object Map Space分配內(nèi)存的邏輯是很簡(jiǎn)單的,直接就是調(diào)用MemMap類(lèi)的靜態(tài)成員函數(shù)MapAnonymous創(chuàng)建1塊指定大小的匿名內(nèi)存,然后再將該匿名同享內(nèi)存添加到成員變量large_objects_描寫(xiě)的1個(gè)向量中去,最后更新內(nèi)部的各個(gè)統(tǒng)計(jì)數(shù)據(jù)。
DlMallocSpace類(lèi)的成員函數(shù)Alloc的實(shí)現(xiàn)以下所示:
DlMallocSpace類(lèi)的成員函數(shù)Alloc調(diào)用另外1個(gè)成員函數(shù)AllocNonvirtual來(lái)進(jìn)行內(nèi)存分配,后者的實(shí)現(xiàn)以下所示:
DlMallocSpace類(lèi)的成員函數(shù)AllocNonvirtual首先是調(diào)用另外1個(gè)成員函數(shù)AllocWithoutGrowthLocked在不增長(zhǎng)Space的大小的條件下進(jìn)行內(nèi)存分配,分配成功以后再調(diào)用函數(shù)memset對(duì)分配出來(lái)的內(nèi)存進(jìn)行清空,最后將分配出來(lái)的內(nèi)存返回給調(diào)用者。
DlMallocSpace類(lèi)的成員函數(shù)AllocWithoutGrowthLocked的實(shí)現(xiàn)以下所示:
從前面ART運(yùn)行時(shí)Java堆創(chuàng)建進(jìn)程分析1文可以知道,DlMallocSpace底層使用的匿名同享內(nèi)存塊被封裝成1個(gè)mspace對(duì)象,并且保存在成員變量mspace_中,因此這里就能夠直接調(diào)用C庫(kù)提供的mspace_malloc接口進(jìn)行內(nèi)存分配。使用mspace_malloc分配的內(nèi)存會(huì)自動(dòng)被清空,因此這里不用再手動(dòng)清空。DlMallocSpace類(lèi)的成員函數(shù)AllocWithoutGrowthLocked在將分配出來(lái)的內(nèi)存返回給調(diào)用者之前,一樣是會(huì)更新內(nèi)部的各個(gè)統(tǒng)計(jì)數(shù)據(jù)。
回到前面Heap類(lèi)的成員函數(shù)Allocate中,在調(diào)用成員函數(shù)TryToAllocate不能成功分配指定大小的內(nèi)存塊以后,接下來(lái)就繼續(xù)調(diào)用成員函數(shù)AllocateInternalWithGc履行帶GC的內(nèi)存分配,它的實(shí)現(xiàn)以下所示:
Heap類(lèi)的成員函數(shù)AllocateInternalWithGc主要是通過(guò)垃圾回收來(lái)滿(mǎn)足要求分配的內(nèi)存,它的履行邏輯以下所示:
1. 調(diào)用Heap類(lèi)的成員函數(shù)WaitForConcurrentGcToComplete檢查是不是有并行GC正在履行。如果有的話(huà),就等待其履行完成,并且得到它的類(lèi)型last_gc。如果last_gc如果不等于collector::kGcTypeNone,就表示有并行GC并且已履行完成,因此就能夠調(diào)用Heap類(lèi)的成員函數(shù)TryToAllocate在不增長(zhǎng)當(dāng)前堆大小的條件下再次嘗試分配要求的內(nèi)存了。如果分配成功,則返回得到的內(nèi)存起始地址給調(diào)用者。否則的話(huà),繼續(xù)往下履行。
2. 順次履行kGcTypeSticky、kGcTypePartial和kGcTypeFull3種類(lèi)型的GC。每次GC履行終了,都嘗試調(diào)用Heap類(lèi)的成員函數(shù)TryToAllocate在不增長(zhǎng)當(dāng)前堆大小的條件下再次嘗試分配要求的內(nèi)存。如果分配內(nèi)存成功,則返回得到的內(nèi)存起始地址給調(diào)用者,并且不再履行下1個(gè)種類(lèi)型的GC。
這里需要注意的1點(diǎn)是,kGcTypeSticky、kGcTypePartial和kGcTypeFull3種類(lèi)型的GC的垃圾回收力度是順次加強(qiáng):kGcTypeSticky只回收上次GC后在Allocation Space中新分配的垃圾對(duì)象;kGcTypePartial只回收Allocation Space的垃圾對(duì)象;kGcTypeFull同時(shí)回收Z(yǔ)ygote Space和Allocation Space的垃圾對(duì)象。通過(guò)這類(lèi)策略,就有可能以最小代價(jià)解決分配對(duì)象時(shí)遇到的內(nèi)在不足問(wèn)題。不過(guò),對(duì)類(lèi)型為kGcTypeSticky和kGcTypePartial的GC,它們的履行是條件的
類(lèi)型為kGcTypeSticky的GC的履行代碼雖然是最小的,但是它能夠回收的垃圾也是最小的。如果回收的垃圾不足于滿(mǎn)足要求分配的內(nèi)存,那就相當(dāng)于做了1次無(wú)用功了。因此,履行類(lèi)型為kGcTypeSticky的GC需要滿(mǎn)足兩個(gè)條件。第1個(gè)條件是上次GC后在Allocation Space上分配的內(nèi)存要到達(dá)1定的閥值,這樣才有比較大的幾率回收到較多的內(nèi)存。第2個(gè)條件Allocation Space剩余的未分配內(nèi)存要到達(dá)1定的閥值,這樣可以保證在回收得到較少內(nèi)存時(shí),也有比較大的幾率滿(mǎn)足要求分配的內(nèi)存。前1個(gè)閥值定義在Heap類(lèi)的成員變量min_alloc_space_size_for_sticky_gc_中,它的值設(shè)置為2M,而上次GC以來(lái)分配的內(nèi)存通過(guò)當(dāng)前Allocation Space的大小估算得到,即通過(guò)調(diào)用Heap類(lèi)的成員變量alloc_space_指向的1個(gè)DlMallocSpace對(duì)象的成員函數(shù)Size取得。后1個(gè)閥值定義在Heap類(lèi)的成員變量min_remaining_space_for_sticky_gc_中,它的值設(shè)置為1M,而Allocation Space剩余的未分配內(nèi)存可以用Allocation Space的總大小減去當(dāng)前Allocation Space的大小得到。通過(guò)調(diào)用Heap類(lèi)的成員變量alloc_space_指向的1個(gè)DlMallocSpace對(duì)象的成員函數(shù)Capacity取得其總大小。
類(lèi)型為kGcTypePartial的GC的履行條件是已從Zygote Space中劃分出Allocation Space。從前面ART運(yùn)行時(shí)Java堆創(chuàng)建進(jìn)程分析1文可以知道,當(dāng)Heap類(lèi)的成員變量have_zygote_space_的值等于true時(shí),就表明已從Zygote Space中劃分出Allocation Space了。因此,在這類(lèi)情況下,就能夠履行類(lèi)型為kGcTypePartial的GC了。
每種類(lèi)型的GC都是通過(guò)調(diào)用Heap類(lèi)的成員函數(shù)CollectGarbageInternal來(lái)履行。注意這時(shí)候候調(diào)用Heap類(lèi)的成員函數(shù)CollectGarbageInternal傳遞的第3個(gè)參數(shù)為false,表示不對(duì)那些只被軟援用對(duì)象援用的對(duì)象進(jìn)行回收。如果上述的3種類(lèi)型的GC履行終了,還是不能滿(mǎn)足分配要求的內(nèi)存,則繼續(xù)往下履行。
3. 經(jīng)過(guò)前面3種類(lèi)型的GC后還是不能成功分配到內(nèi)存,那就說(shuō)明能夠回收的內(nèi)存還是太小了,因此,這時(shí)候候只能通過(guò)在允許范圍內(nèi)增長(zhǎng)堆的大小來(lái)滿(mǎn)足內(nèi)存分配要求了。前面分析Heap類(lèi)的成員函數(shù)TryToAlloctate時(shí),將第4個(gè)參數(shù)設(shè)置為true,便可在允許范圍內(nèi)增長(zhǎng)堆大小的條件下進(jìn)行內(nèi)存分配。如果在允許范圍內(nèi)增長(zhǎng)了堆的大小還是不能成功分配到要求的內(nèi)存,那就只能出最后的1個(gè)大招了。
4. 最后的大招是首先履行1個(gè)類(lèi)型為kGcTypeFull的、要求回收那些只被軟援用對(duì)象援用的對(duì)象的GC,接著再在允許范圍內(nèi)增長(zhǎng)堆大小的條件下嘗試分配內(nèi)存。這1次如果還是失敗,那就真的是內(nèi)存不足了。
至此,我們就對(duì)ART運(yùn)行時(shí)在堆上為新創(chuàng)建對(duì)象分配內(nèi)存的進(jìn)程分析完成了。從中我們就能夠看到,ART運(yùn)行時(shí)面臨的最大挑戰(zhàn)就是內(nèi)存不足問(wèn)題,它要通過(guò)在允許范圍內(nèi)增長(zhǎng)堆大小和垃圾回收兩個(gè)手段來(lái)解決。其中,垃圾回收會(huì)對(duì)程序造成影響,因此在履行垃圾回收時(shí),使用的力度要從小到大。在接下來(lái)的1篇文章中,我們就將詳細(xì)分析這些力度不同的垃圾回收是如何實(shí)現(xiàn)的,敬請(qǐng)關(guān)注!更多的信息也能夠關(guān)注老羅的新浪微博:http://weibo.com/shengyangluo。