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