版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
/ART運行時垃圾收集(GC)過程分析ART運行時與Dalvik虛擬機一樣,都使用了Mark-Sweep算法進行垃圾回收,因此它們的垃圾回收流程在總體上是一致的。但是ART運行時對堆的劃分更加細致,因而在此基礎上實現(xiàn)了更多樣的回收策略。不同的策略有不同的回收力度,力度越大的回收策略,每次回收的內存就越多,并且它們都有各自的使用情景。這樣就可以使得每次執(zhí)行GC時,可以最大限度地減少應用程序停頓。本文就詳細分析ART運行時的垃圾收集過程。ART運行時的垃圾收集收集過程如圖1所示:圖1的最上面三個箭頭描述觸發(fā)GC的三種情況,左邊的流程圖描述非并行GC的執(zhí)行過程,右邊的流程圖描述并行GC的執(zhí)行流程,接下來我們就詳細圖中涉及到的所有細節(jié)。在前面一文中,我們提到了兩種可能會觸發(fā)GC的情況。第一種情況是沒有足夠內存分配請求的分存時,會調用Heap類的成員函數(shù)CollectGarbageInternal觸發(fā)一個原因為kGcCauseForAlloc的GC。第二種情況下分配出請求的內存之后,堆剩下的內存超過一定的閥值,就會調用Heap類的成員函數(shù)RequestConcurrentGC請求執(zhí)行一個并行GC。Heap類的成員函數(shù)RequestConcurrentGC的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidHeap::RequestConcurrentGC(Thread*self){//MakesurethatwecandoaconcurrentGC.Runtime*runtime=Runtime::Current();DCHECK(concurrent_gc_);if(runtime==NULL||!runtime->IsFinishedStarting()||!runtime->IsConcurrentGcEnabled()){return;}{MutexLockmu(self,*Locks::runtime_shutdown_lock_);if(runtime->IsShuttingDown()){return;}}if(self->IsHandlingStackOverflow()){return;}//Wealreadyhavearequestpending,noreasontostartmoreuntilweupdate//concurrent_start_bytes_.concurrent_start_bytes_=std::numeric_limits<size_t>::max();JNIEnv*env=self->GetJniEnv();DCHECK(WellKnownClasses::java_lang_Daemons!=NULL);DCHECK(WellKnownClasses::java_lang_Daemons_requestGC!=NULL);env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,WellKnownClasses::java_lang_Daemons_requestGC);CHECK(!env->ExceptionCheck());}這個函數(shù)定義在文件art/runtime/gc/heap.cc。只有滿足以下四個條件,Heap類的成員函數(shù)RequestConcurrentGC才會觸發(fā)一個并行GC:1.ART運行時已經(jīng)啟動完畢。2.ART運行時支持并行GC。ART運行時默認是支持并行GC的,但是可以通過啟動選項-Xgc來關閉。3.ART運行時不是正在關閉。4.當前線程沒有發(fā)生棧溢出。上述4個條件都滿足之后,Heap類的成員函數(shù)RequestConcurrentGC就將成員變量concurrent_start_bytes_的值設置為類型size_t的最大值,表示目前正有一個并行GC在等待執(zhí)行,以阻止觸發(fā)另外一個并行GC。最后,Heap類的成員函數(shù)RequestConcurrentGC調用Java層的java.lang.Daemons類的靜態(tài)成員函數(shù)requestGC請求執(zhí)行一次并行GC。Java層的java.lang.Daemons類在加載的時候,會啟動五個與堆或者GC相關的守護線程,如下所示:[java]viewplaincopy在CODE上查看代碼片派生到我的代碼片publicfinalclassDaemons{publicstaticvoidstart(){ReferenceQueueDaemon.INSTANCE.start();FinalizerDaemon.INSTANCE.start();FinalizerWatchdogDaemon.INSTANCE.start();HeapTrimmerDaemon.INSTANCE.start();GCDaemon.INSTANCE.start();}}這個類定義在文件libcore/libart/src/main/java/java/lang/Daemons.java中。這五個守護線程分別是:1.ReferenceQueueDaemon:引用隊列守護線程。我們知道,在創(chuàng)立引用對象的時候,可以關聯(lián)一個隊列。當被引用對象引用的對象被GC回收的時候,被引用對象就會被參加到其創(chuàng)立時關聯(lián)的隊列去。這個參加隊列的操作就是由ReferenceQueueDaemon守護線程來完成的。這樣應用程序就可以知道那些被引用對象引用的對象已經(jīng)被回收了。2.FinalizerDaemon:析構守護線程。對于重寫了成員函數(shù)finalize的對象,它們被GC決定回收時,并沒有馬上被回收,而是被放入到一個隊列中,等待FinalizerDaemon守護線程去調用它們的成員函數(shù)finalize,然后再被回收。3.FinalizerWatchdogDaemon:析構監(jiān)護守護線程。用來監(jiān)控FinalizerDaemon線程的執(zhí)行。一旦檢測那些重定了成員函數(shù)finalize的對象在執(zhí)行成員函數(shù)finalize時超出一定的時候,那么就會退出VM。4.HeapTrimmerDaemon:堆裁剪守護線程。用來執(zhí)行裁剪堆的操作,也就是用來將那些空閑的堆內存歸還給系統(tǒng)。5.GCDaemon:并行GC線程。用來執(zhí)行并行GC。Java層的java.lang.Daemons類的靜態(tài)成員函數(shù)requestGC被調用時,就會喚醒上述的并行GC線程,然后這個并行GC線程就會通過JNI調用Heap類的成員函數(shù)ConcurrentGC,它的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidHeap::ConcurrentGC(Thread*self){{MutexLockmu(self,*Locks::runtime_shutdown_lock_);if(Runtime::Current()->IsShuttingDown()){return;}}//WaitforanyGCscurrentlyrunningtofinish.if(WaitForConcurrentGcToComplete(self)==collector::kGcTypeNone){CollectGarbageInternal(next_gc_type_,kGcCauseBackground,false);}}這個函數(shù)定義在文件art/runtime/gc/heap.cc中。只要ART運行時當前不是處于正在關閉的狀態(tài),那么Heap類的成員函數(shù)ConcurrentGC就會檢查當前是否正在執(zhí)行GC。如果是的話,那么就等待它執(zhí)行完成,然后再調用Heap類的成員函數(shù)CollectGarbageInternal觸發(fā)一個原因為kGcCauseBackground的GC。否則的話,就直接調用Heap類的成員函數(shù)CollectGarbageInternal觸發(fā)一個原因為kGcCauseBackground的GC。從這里就可以看到,無論是觸發(fā)GC的原因是kGcCauseForAlloc,還是kGcCauseBackground,最終都是通過調用Heap類的成員函數(shù)CollectGarbageInternal來執(zhí)行GC的。此外,還有第三種情況會觸發(fā)GC,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidHeap::CollectGarbage(boolclear_soft_references){//EvenifwewaitedforaGCwestillneedtodoanotherGCsinceweaksallocatedduringthe//lastGCwillnothavenecessarilybeencleared.Thread*self=Thread::Current();WaitForConcurrentGcToComplete(self);CollectGarbageInternal(collector::kGcTypeFull,kGcCauseExplicit,clear_soft_references);}這個函數(shù)定義在文件art/runtime/gc/heap.cc。當我們調用Java層的java.lang.System的靜態(tài)成員函數(shù)gc時,如果ART運行時支持顯式GC,那么就它就會通過JNI調用Heap類的成員函數(shù)CollectGarbageInternal來觸發(fā)一個原因為kGcCauseExplicit的GC。ART運行時默認是支持顯式GC的,但是可以通過啟動選項-XX:+DisableExplicitGC來關閉。從上面的分析就可以看出,ART運行時在三種情況下會觸發(fā)GC,這三種情況通過三個枚舉kGcCauseForAlloc、kGcCauseBackground和kGcCauseExplicitk來描述。這三人枚舉的定義如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片//WhatcausedtheGC?enumGcCause{//GCtriggeredbyafailedallocation.ThreaddoingallocationisblockedwaitingforGCbefore//retryingallocation.kGcCauseForAlloc,//AbackgroundGCtryingtoensurethereisfreememoryaheadofallocations.kGcCauseBackground,//AnexplicitSystem.gc()call.kGcCauseExplicit,};這三個枚舉定義在文件art/runtime/gc/heap.h中。從上面的分析還可以看出,ART運行時的所有GC都是以Heap類的成員函數(shù)CollectGarbageInternal為入口,它的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片collector::GcTypeHeap::CollectGarbageInternal(collector::GcTypegc_type,GcCausegc_cause,boolclear_soft_references){Thread*self=Thread::Current();//EnsurethereisonlyoneGCatatime.boolstart_collect=false;while(!start_collect){{MutexLockmu(self,*gc_complete_lock_);if(!is_gc_running_){is_gc_running_=true;start_collect=true;}}if(!start_collect){//TODO:timinglogthis.WaitForConcurrentGcToComplete(self);}}if(gc_type==collector::kGcTypeSticky&&alloc_space_->Size()<min_alloc_space_size_for_sticky_gc_){gc_type=collector::kGcTypePartial;}collector::MarkSweep*collector=NULL;for(constauto&cur_collector:mark_sweep_collectors_){if(cur_collector->IsConcurrent()==concurrent_gc_&&cur_collector->GetGcType()==gc_type){collector=cur_collector;break;}}collector->clear_soft_references_=clear_soft_references;collector->Run();{MutexLockmu(self,*gc_complete_lock_);is_gc_running_=false;last_gc_type_=gc_type;//WakeanyonewhomayhavebeenwaitingfortheGCtocomplete.gc_complete_cond_->Broadcast(self);}returngc_type;}這個函數(shù)定義在文件art/runtime/gc/heap.cc。參數(shù)gc_type和gc_cause分別用來描述要執(zhí)行的GC的類型和原因,而參數(shù)clear_soft_references用來描述是否要回收被軟引用對象引用的對象。Heap類的成員函數(shù)CollectGarbageInternal的執(zhí)行邏輯如下所示:1.通過一個while循環(huán)不斷地檢查Heap類的成員變量is_gc_running_,直到它的值等于false為止,這表示當前沒有其它線程正在執(zhí)行GC。當它的值等于true時,就表示在其它線程正在執(zhí)行GC,這時候就要調用Heap類的成員函數(shù)WaitForConcurrentGcToComplete等待其執(zhí)行完成。注意,在當前GC執(zhí)行之前,Heap類的成員變量is_gc_running_會被設置為true。2.如果當前請求執(zhí)行的GC的類型為kGcTypeSticky,但是當前AllocationSpace的大小小于Heap類的成員變量min_alloc_space_size_for_sticky_gc_指定的閥值,那么就改為執(zhí)行類型為kGcTypePartial。關于類型為kGcTypeSticky的GC的執(zhí)行限制,可以參數(shù)前面一文。3.從Heap類的成員變量mark_sweep_collectors_指向的一個垃圾收集器列表找到一個適宜的垃圾收集器來執(zhí)行GC。從前面一文可以知道,ART運行時在內部創(chuàng)立了六個垃圾收集器。這六個垃圾收集器分為兩組,一組支持并行GC,另一組不支持。每一組都是由三個類型分別為kGcTypeSticky、kGcTypePartial和kGcTypeFull的垃垃圾收集器組成。這里說的適宜的垃圾收集器,是指并行性與Heap類的成員變量concurrent_gc_一致,并且類型也與參數(shù)gc_type一致的垃圾收集器。4.找到適宜的垃圾收集器之后,就將參數(shù)clear_soft_references的值保存它的成員變量clear_soft_references_中,以便可以告訴它要不要回收被軟引用對象引用的對象,然后再調用它的成員函數(shù)Run來執(zhí)行GC。5.GC執(zhí)行完畢,將Heap類的成員變量is_gc_running_設置為false,以表示當前GC已經(jīng)執(zhí)行完畢,下一次請求的GC可以執(zhí)行了。此外,也會將Heap類的成員變量last_gc_type_設置為當前執(zhí)行的GC的類型。這樣下一次執(zhí)行GC時,就可以執(zhí)行另外一個不同類型的GC。例如,如果上一次執(zhí)行的GC的類型為kGcTypeSticky,那么接下來的兩次GC的類型就可以設置為kGcTypePartial和kGcTypeFull,這樣可以使得每次都能執(zhí)行有效的GC。6.通過Heap類的成員變量gc_complete_cond_喚醒那些正在等待GC執(zhí)行完成的線程。在上面的六個步驟中,最重要的就是第四步了。從前面一文可以知道,所有的垃圾收集器都是從GarbageCollector類繼承下來的,因此上面的第四步實際上執(zhí)行的是GarbageCollector類的成員函數(shù)Run,它的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidGarbageCollector::Run(){ThreadList*thread_list=Runtime::Current()->GetThreadList();uint64_tstart_time=NanoTime();pause_times_.clear();duration_ns_=0;InitializePhase();if(!IsConcurrent()){//PauseistheentirelengthoftheGC.uint64_tpause_start=NanoTime();ATRACE_BEGIN("Applicationthreadssuspended");thread_list->SuspendAll();MarkingPhase();ReclaimPhase();thread_list->ResumeAll();ATRACE_END();uint64_tpause_end=NanoTime();pause_times_.push_back(pause_end-pause_start);}else{Thread*self=Thread::Current();{ReaderMutexLockmu(self,*Locks::mutator_lock_);MarkingPhase();}booldone=false;while(!done){uint64_tpause_start=NanoTime();ATRACE_BEGIN("Suspendingmutatorthreads");thread_list->SuspendAll();ATRACE_END();ATRACE_BEGIN("Allmutatorthreadssuspended");done=HandleDirtyObjectsPhase();ATRACE_END();uint64_tpause_end=NanoTime();ATRACE_BEGIN("Resumingmutatorthreads");thread_list->ResumeAll();ATRACE_END();pause_times_.push_back(pause_end-pause_start);}{ReaderMutexLockmu(self,*Locks::mutator_lock_);ReclaimPhase();}}uint64_tend_time=NanoTime();duration_ns_=end_time-start_time;FinishPhase();}這個函數(shù)定義在文件art/runtime/gc/collector/garbage_collector.cc中。GarbageCollector類的成員函數(shù)Run的實現(xiàn)就對應于圖1所示的左邊和右邊的兩個流程。圖1所示的左邊流程是用來執(zhí)行非并行GC的,過程如下所示:1.調用子類實現(xiàn)的成員函數(shù)InitializePhase執(zhí)行GC初始化階段。2.掛起所有的ART運行時線程。3.調用子類實現(xiàn)的成員函數(shù)MarkingPhase執(zhí)行GC標記階段。4.調用子類實現(xiàn)的成員函數(shù)ReclaimPhase執(zhí)行GC回收階段。5.恢復第2步掛起的ART運行時線程。6.調用子類實現(xiàn)的成員函數(shù)FinishPhase執(zhí)行GC結束階段。圖1所示的右邊流程是用來執(zhí)行并行GC的,過程如下所示:1.調用子類實現(xiàn)的成員函數(shù)InitializePhase執(zhí)行GC初始化階段。2.獲取用于訪問Java堆的鎖。3.調用子類實現(xiàn)的成員函數(shù)MarkingPhase執(zhí)行GC并行標記階段。4.釋放用于訪問Java堆的鎖。5.掛起所有的ART運行時線程。6.調用子類實現(xiàn)的成員函數(shù)HandleDirtyObjectsPhase處理在GC并行標記階段被修改的對象。。7.恢復第4步掛起的ART運行時線程。8.重復第5到第7步,直到所有在GC并行階段被修改的對象都處理完成。9.獲取用于訪問Java堆的鎖。10.調用子類實現(xiàn)的成員函數(shù)ReclaimPhase執(zhí)行GC回收階段。11.釋放用于訪問Java堆的鎖。12.調用子類實現(xiàn)的成員函數(shù)FinishPhase執(zhí)行GC結束階段。從上面的分析就可以看出,并行GC和非并行GC的區(qū)別在于:1.非并行GC的標記階段和回收階段是在掛住所有的ART運行時線程的前提下進行的,因此,只需要執(zhí)行一次標記即可。2.并行GC的標記階段只鎖住了Java堆,因此它不能阻止那些不是正在分配對象的ART運行時線程同時運行,而這些同進運行的ART運行時線程可能會引用了一些在之前的標記階段沒有被標記的對象。如果不對這些對象進行重新標記的話,那么就會導致它們被GC回收,造成錯誤。因此,與非并行GC相比,并行GC多了一個處理臟對象的階段。所謂的臟對象就是我們前面說的在GC標記階段同時運行的ART運行時線程訪問或者修改正的對象。3.并行GC并不是自始至終都是并行的,例如,處理臟對象的階段就是需要掛起除GC線程以外的其它ART運行時線程,這樣才可以保證標記階段可以結束。從前面一文可以知道,GarbageCollector類有三個直接或者間接的子類MarkSweep、PartialMarkSweep和StickyMarkSweep都可以用來執(zhí)行垃圾回收,其中,PartialMarkSweep類又是從MarkSweep類直接繼承下來的,而StickyMarkSweep類是從PartialMarkSweep類直接繼承下來的。MarkSweep類用來回收ZygoteSpace和AllocationSpace的垃圾,PartialMarkSweep類用來回收AllocationSpace的垃圾,StickyMarkSweep類用來回收上次GC以來在AllcationSpace上分配的最終又沒有被引用的垃圾。接下來,我們就主要分析ART運行時線程的掛起和恢復過程,以及MarkSweep、PartialMarkSweep和StickyMarkSweep這三個類是執(zhí)行InitializePhase、MarkingPhase、HandleDirtyObjectsPhase、ReclaimPhase和FinishPhase的五個GC階段的過程。1.ART運行時線程的掛起從上面的分析可以知道,ART運行時線程的掛起是通過調用ThreadList類的成員函數(shù)SuspendAll實現(xiàn)的,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidThreadList::SuspendAll(){Thread*self=Thread::Current();{MutexLockmu(self,*Locks::thread_list_lock_);{MutexLockmu2(self,*Locks::thread_suspend_count_lock_);//Updateglobalsuspendallstateforattachingthreads.++suspend_all_count_;//Incrementeverybody'ssuspendcount(exceptourown).for(constauto&thread:list_){if(thread==self){continue;}thread->ModifySuspendCount(self,+1,false);}}}//BlockonthemutatorlockuntilallRunnablethreadsreleasetheirshareofaccess.#ifHAVE_TIMED_RWLOCK//Timeoutifwewaitmorethan30seconds.if(UNLIKELY(!Locks::mutator_lock_->ExclusiveLockWithTimeout(self,30*1000,0))){UnsafeLogFatalForThreadSuspendAllTimeout(self);}#elseLocks::mutator_lock_->ExclusiveLock(self);#endif}這個函數(shù)定義在文件art/runtime/thread_list.cc中。所有的ART運行時線程都保存在ThreadList類的成員變量list_描述的一個列表,遍歷這個列表時,需要獲取Lock類的成員變量thread_list_lock_描述的一個互斥鎖。ThreadList類有一個成員變量suspend_all_count_,用來描述全局的線程掛起計數(shù)器。在所有的ART運行時線程掛起期間,如果有新的線程將自己注冊為ART運行時線程,那么它也會將自己掛起來,而判斷所有的ART運行時線程是不是處于掛起期間,就是通過ThreadList類的成員變量suspend_all_count_的值是否大于0進行的。因此,ThreadList類的成員函數(shù)SuspendAll在掛起所有的ART運行時線程之前,會將ThreadList類的成員變量suspend_all_count_的值增加1。接下來,ThreadList類的成員函數(shù)SuspendAll遍歷所有的ART運行時線程,并且調用Thread類的成員函數(shù)ModifySuspendCount將它內部的線程計算數(shù)器增加1,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidThread::AtomicSetFlag(ThreadFlagflag){android_atomic_or(flag,&state_and_flags_.as_int);}voidThread::AtomicClearFlag(ThreadFlagflag){android_atomic_and(-1^flag,&state_and_flags_.as_int);}voidThread::ModifySuspendCount(Thread*self,intdelta,boolfor_debugger){suspend_count_+=delta;if(suspend_count_==0){AtomicClearFlag(kSuspendRequest);}else{AtomicSetFlag(kSuspendRequest);}}這三個函數(shù)定義在文件art/runtime/thread.cc中。Thread類的成員函數(shù)ModifySuspendCount的實現(xiàn)很簡單,它主要就是將成員變量suspend_count_的值增加delta,并且判斷增加后的值是否等于0。如果等于0,就調用成員函數(shù)AtomicClearFlag將另外一個成員變量state_and_flags_的int值的kSuspendRequest位清0,表示線程沒有掛起請求。否則的話,就調用成員函數(shù)AtomicSetFlag將成員變量state_and_flags_的int值的kSuspendRequest位置1,表示線程有掛起請求?;氐角懊鎀hreadList類的成員函數(shù)SuspendAll中,全局ART運行時線程掛起計數(shù)器和每一個ART運行時線程內部的線程掛起計數(shù)器的操作都是需要在獲取Locks類的靜態(tài)成員變量thread_suspend_count_lock_描述的一個互斥鎖的前提下進行的。最后,ThreadList類的成員函數(shù)SuspendAll通過獲取Locks類的靜態(tài)成員變量mutator_lock_描述的一個讀寫鎖的寫訪問來等待所有的ART運行時線程掛起的。這是如何做到的呢?在前面一文中,我們提到,ART運行時提供應由DEX字節(jié)碼翻譯而來的本地機器代碼使用的一個函數(shù)表中,包含了一個pCheckSuspend函數(shù)指針,該函數(shù)指針指向了函數(shù)CheckSuspendFromCode。于是,每一個ART運行時線程在執(zhí)行本地機器代碼的過程中,就會周期性地通過調用函數(shù)CheckSuspendFromCode來檢查自己是否需要掛起。這一點與前面一文分析的Dalvik虛擬機線程掛起的過程是類似的。函數(shù)CheckSuspendFromCode的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidCheckSuspendFromCode(Thread*thread)SHARED_LOCKS_REQUIRED(Locks::mutator_lock_){CheckSuspend(thread);}這個函數(shù)定義在文件art/runtime/entrypoints/quick/quick_thread_entrypoints.cc中。函數(shù)CheckSuspendFromCode調用另外一個函數(shù)CheckSuspend檢查當前線程是否需要掛起,后者的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片staticinlinevoidCheckSuspend(Thread*thread)SHARED_LOCKS_REQUIRED(Locks::mutator_lock_){for(;;){if(thread->ReadFlag(kCheckpointRequest)){thread->RunCheckpointFunction();thread->AtomicClearFlag(kCheckpointRequest);}elseif(thread->ReadFlag(kSuspendRequest)){thread->FullSuspendCheck();}else{break;}}}這個函數(shù)定義在文件art/runtime/entrypoints/entrypoint_utils.h中。從上面的分析可以知道,如果當前線程的線程掛起計數(shù)器不等于0,那么它內部的一個標記位kSuspendRequest被設置為1。這時候函數(shù)CheckSuspend就會調用Thread類的成員函數(shù)FullSuspendCheck來將自己掛起。此外,函數(shù)CheckSuspend還會檢查線程內部的另外一個標記位kCheckpointRequest是否被設置為1。如果被設置為1的話,那么就說明線程有一個CheckPoint需要執(zhí)行,這時候就會先調用Thread類的成員函數(shù)RunCheckpointFunction運行該CheckPoint,接著再將線程內部的標記位kCheckpointRequest清0。關于線程的CheckPoint,我們后面再分析。Thread類的成員函數(shù)FullSuspendCheck的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidThread::FullSuspendCheck(){//Makethreadappearsuspendedtootherthreads,releasemutator_lock_.TransitionFromRunnableToSuspended(kSuspended);//Transitionbacktorunnablenotingrequeststosuspend,re-acquireshareonmutator_lock_.TransitionFromSuspendedToRunnable();}這個函數(shù)定義在文件art/runtime/thread.cc中。Thread類的成員函數(shù)FullSuspendCheck首先是調用成員函數(shù)TransitionFromRunnableToSuspended將自己從運行狀態(tài)修改為掛起狀態(tài),接著再調用成員函數(shù)TransitionFromSuspendedToRunnable將自己從掛起狀態(tài)修改為運行狀態(tài)。通過Thread類的這兩個神奇的成員函數(shù),就實現(xiàn)了將當前線程掛起的功能。接下來我們就繼續(xù)分析它們是怎么實現(xiàn)的。Thread類的成員函數(shù)TransitionFromRunnableToSuspended的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片inlinevoidThread::TransitionFromRunnableToSuspended(ThreadStatenew_state){unionStateAndFlagsold_state_and_flags;unionStateAndFlagsnew_state_and_flags;do{old_state_and_flags=state_and_flags_;//Copyoverflagsandtrytoclearthecheckpointbitifitisset.new_state_and_flags.as_struct.flags=old_state_and_flags.as_struct.flags&~kCheckpointRequest;new_state_and_flags.as_struct.state=new_state;//CASthevaluewithoutamemorybarrier,thatwilloccurintheunlockbelow.}while(UNLIKELY(android_atomic_cas(old_state_and_flags.as_int,new_state_and_flags.as_int,&state_and_flags_.as_int)!=0));//Ifwetoggledthecheckpointflagwemusthaveclearedit.uint16_tflag_change=new_state_and_flags.as_struct.flags^old_state_and_flags.as_struct.flags;if(UNLIKELY((flag_change&kCheckpointRequest)!=0)){RunCheckpointFunction();}//Releaseshareonmutator_lock_.Locks::mutator_lock_->SharedUnlock(this);}這個函數(shù)定義在文件art/runtime/thread-inl.h中。線程的狀態(tài)和標記位均保存在Thread類的成員變量state_and_flags_描述的一個Union結構體中。Thread類的成員函數(shù)TransitionFromRunnableToSuspended首先是將線程舊的狀態(tài)和標志位保存起來,然后再清空它的kCheckpointRequest標志位,以及將它的狀態(tài)設置為參數(shù)new_state描述的狀態(tài),即kSuspended狀態(tài)。設置好線程新的標記位和狀態(tài)之后,Thread類的成員函數(shù)TransitionFromRunnableToSuspended再檢查線程原來的標記位kCheckpointRequest是否等于1。如果等于1的話,那么在釋放Locks類的靜態(tài)成員變量mutator_lock_描述的一個讀寫鎖的讀訪問之前,先調用Thread類的成員函數(shù)RunCheckpointFunction來執(zhí)行線程的CheckPoint。Thread類的成員函數(shù)TransitionFromSuspendedToRunnable的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片inlineThreadStateThread::TransitionFromSuspendedToRunnable(){booldone=false;unionStateAndFlagsold_state_and_flags=state_and_flags_;int16_told_state=old_state_and_flags.as_struct.state;do{old_state_and_flags=state_and_flags_;if(UNLIKELY((old_state_and_flags.as_struct.flags&kSuspendRequest)!=0)){//Waitwhileoursuspendcountisnon-zero.MutexLockmu(this,*Locks::thread_suspend_count_lock_);old_state_and_flags=state_and_flags_;while((old_state_and_flags.as_struct.flags&kSuspendRequest)!=0){//Re-checkwhenThread::resume_cond_isnotified.Thread::resume_cond_->Wait(this);old_state_and_flags=state_and_flags_;}}//Re-acquiresharedmutator_lock_access.Locks::mutator_lock_->SharedLock(this);//Atomicallychangefromsuspendedtorunnableifnosuspendrequestpending.old_state_and_flags=state_and_flags_;if(LIKELY((old_state_and_flags.as_struct.flags&kSuspendRequest)==0)){unionStateAndFlagsnew_state_and_flags=old_state_and_flags;new_state_and_flags.as_struct.state=kRunnable;//CASthevaluewithoutamemorybarrier,thatoccurredinthelockabove.done=android_atomic_cas(old_state_and_flags.as_int,new_state_and_flags.as_int,&state_and_flags_.as_int)==0;}if(UNLIKELY(!done)){//FailedtotransitiontoRunnable.Releasesharedmutator_lock_accessandtryagain.Locks::mutator_lock_->SharedUnlock(this);}}while(UNLIKELY(!done));returnstatic_cast<ThreadState>(old_state);}這個函數(shù)定義在文件art/runtime/thread-inl.h中。Thread類的成員函數(shù)TransitionFromSuspendedToRunnable的實現(xiàn)很簡單,它通過一個do...while循環(huán)不斷地判斷自己是否有一個掛起請求。如果有的話,就在Thread類的靜態(tài)成員變量resume_cond_描述的一個條件變量上進行等待,直到被其它線程喚醒為止。被喚醒之后,又在獲取Locks類的靜態(tài)成員變量mutator_lock_描述的一個讀寫鎖的讀訪問的前提下,將自己的狀態(tài)設置為運行狀態(tài),即kRunnable。如果設置成功,就結束執(zhí)行do...while循環(huán)。否則的話,就說明又可能有其它線程請求將自己掛起,因此Thread類的成員函數(shù)TransitionFromSuspendedToRunnable又需要釋放之前獲得的讀寫鎖的讀訪問,然后再重新執(zhí)行一遍do...while循環(huán)。Thread類的成員函數(shù)TransitionFromRunnableToSuspended和TransitionFromSuspendedToRunnable均是在獲得Locks類的靜態(tài)成員變量mutator_lock_描述的讀寫鎖的讀訪問的前提下執(zhí)行的。假設執(zhí)行GC的線程在調用ThreadList類的成員函數(shù)SuspendAll獲得Locks類的靜態(tài)成員變量mutator_lock_描述的讀寫鎖的寫訪問之前,當前線程還沒有獲得該讀寫鎖的讀訪問,那么很明顯當它要獲得讀訪問時,就會進入等待狀態(tài),因為讀寫鎖的讀訪問和寫訪問是互斥的。另一方面,如果執(zhí)行GC的線程在調用ThreadList類的成員函數(shù)SuspendAll獲得Locks類的靜態(tài)成員變量mutator_lock_描述的讀寫鎖的寫訪問之前,當前線程已經(jīng)獲得了該讀寫鎖的讀訪問,那么隨后它就會調用Thread類的成員函數(shù)TransitionFromRunnableToSuspended釋放對該讀寫鎖的讀訪問,因此GC線程就可以獲得該讀寫鎖的寫訪問。等到當前線程再調用Thread類的成員函數(shù)TransitionFromSuspendedToRunnable獲得該讀寫鎖的讀訪問時,就會進入等待狀態(tài)了。2.ART運行時線程的恢復理解了ART運行時線程的掛起過程之后,再理解它們的恢復狀態(tài)就容易多了,這是通過調用ThreadList類的成員函數(shù)ResumeAll實現(xiàn)的,如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidThreadList::ResumeAll(){Thread*self=Thread::Current();Locks::mutator_lock_->ExclusiveUnlock(self);{MutexLockmu(self,*Locks::thread_list_lock_);MutexLockmu2(self,*Locks::thread_suspend_count_lock_);//Updateglobalsuspendallstateforattachingthreads.--suspend_all_count_;//Decrementthesuspendcountsforallthreads.for(constauto&thread:list_){if(thread==self){continue;}thread->ModifySuspendCount(self,-1,false);}//Broadcastanotificationtoallsuspendedthreads,someorallof//whichmaychoosetowakeup.Noneedtowaitforthem.Thread::resume_cond_->Broadcast(self);}}這個函數(shù)定義在文件art/runtime/thread_list.cc中。ThreadList類的成員函數(shù)ResumeAll所做的事情剛好與成員函數(shù)SuspendAll相反,它首先是釋放Locks類的靜態(tài)成員函數(shù)mutator_lock_描述的讀寫鎖的寫訪問,接著再減少ART運行時線程全局掛起計數(shù)器以及每一個ART運行時線程內部的掛起計數(shù)器,最再喚醒等待在Thread類的靜態(tài)成員變量resume_cond_描述的一個條件變量上的其它ART運行時線程。3.InitializePhaseMarkSweep、PartialMarkSweep和StickyMarkSweep三個類的初始化階段都是相同的,都是由MarkSweep類的成員函數(shù)InitializePhase來實現(xiàn),如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidMarkSweep::InitializePhase(){timings_.Reset();base::TimingLogger::ScopedSplitsplit("InitializePhase",&timings_);mark_stack_=heap_->mark_stack_.get();DCHECK(mark_stack_!=nullptr);SetImmuneRange(nullptr,nullptr);soft_reference_list_=nullptr;weak_reference_list_=nullptr;finalizer_reference_list_=nullptr;phantom_reference_list_=nullptr;cleared_reference_list_=nullptr;freed_bytes_=0;freed_large_object_bytes_=0;freed_objects_=0;freed_large_objects_=0;class_count_=0;array_count_=0;other_count_=0;large_object_test_=0;large_object_mark_=0;classes_marked_=0;overhead_time_=0;work_chunks_created_=0;work_chunks_deleted_=0;reference_count_=0;java_lang_Class_=Class::GetJavaLangClass();CHECK(java_lang_Class_!=nullptr);FindDefaultMarkBitmap();//DoanypreGCverification.timings_.NewSplit("PreGcVerification");heap_->PreGcVerification(this);}這個函數(shù)定義在文件art/runtime/gc/collector/mark_sweep.cc中。MarkSweep類的成員函數(shù)InitializePhase主要就是重置一下接下來GC要用到的一些成員變量,其中,比較重要的就是調用另外一個成員函數(shù)FindDefaultMarkBitmap獲取一個DefaultMarkBitmap,它的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidMarkSweep::FindDefaultMarkBitmap(){base::TimingLogger::ScopedSplitsplit("FindDefaultMarkBitmap",&timings_);for(constauto&space:GetHeap()->GetContinuousSpaces()){if(space->GetGcRetentionPolicy()==space::kGcRetentionPolicyAlwaysCollect){current_mark_bitmap_=space->GetMarkBitmap();CHECK(current_mark_bitmap_!=NULL);return;}}}這個函數(shù)定義在文件art/runtime/gc/collector/mark_sweep.cc中。從這里的就可以看出,DefaultMarkBitmap就是回收策略為kGcRetentionPolicyAlwaysCollect的Space對應的MarkBitmap。從前面一文可以知道,這個Space就是AllocationSpace(如果AllocationSpace還沒有從ZygoteSpace劃分出來,那就是ZygoteSpace)。獲得的DefaultMarkBitmap保存在MarkSweep類的成員變量current_mark_bitmap_中。4.MarkingPhaseMarkSweep、PartialMarkSweep和StickyMarkSweep三個類的標記階段都是以MarkSweep類的成員函數(shù)MarkingPhase為入口,它的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidMarkSweep::MarkingPhase(){base::TimingLogger::ScopedSplitsplit("MarkingPhase",&timings_);Thread*self=Thread::Current();BindBitmaps();FindDefaultMarkBitmap();//Processdirtycardsandadddirtycardstomoduniontables.heap_->ProcessCards(timings_);//Needtodothisbeforethecheckpointsincewedon'twantanythreadstoaddreferencesto//thelivestackduringtherecursivemark.timings_.NewSplit("S");heap_->S();WriterMutexLockmu(self,*Locks::heap_bitmap_lock_);if(Locks::mutator_lock_->IsExclusiveHeld(self)){//Ifweexclusivelyholdthemutatorlock,allthreadsmustbesuspended.MarkRoots();}else{MarkThreadRoots(self);//Atthispointthelivestackshouldnolongerhaveanymutatorswhichpushintoit.MarkNonThreadRoots();}live_stack_freeze_size_=heap_->GetLiveStack()->Size();MarkConcurrentRoots();heap_->UpdateAndMarkModUnion(this,timings_,GetGcType());MarkReachableObjects();}這個函數(shù)定義在文件art/runtime/gc/collector/mark_sweep.cc中。MarkSweep類的成員函數(shù)MarkingPhase標記對象的過程如下所示:A.調用成員函數(shù)BindBitmap設置回收范圍。B.調用成員函數(shù)FindDefaultMarkBitmap找到回收策略為kGcRetentionPolicyAlwaysCollect的Space對應的MarkBitmap,并且保存在成員變量current_mark_bitmap_中。這個函數(shù)我們在前面已經(jīng)分析過了。C.調用Heap類的成員函數(shù)ProcessCards處理CardTable中的DirtyCard,以及這些DirtyCard添加到對應的ModUnionTable中去。D.調用Heap類的成員函數(shù)S交換ART運行時的AllocationStack和LiveStack。E.對于非并行GC,當前線程在掛起其它ART運行時線程的過程中,已經(jīng)獲得Locks類的靜態(tài)成員變量mutator_lock_描述的讀寫鎖的寫訪問,因此這時候就調用成員函數(shù)MarkRoots來標記那些不可以在沒有獲得Locks類的靜態(tài)成員變量mutator_lock_描述的讀寫鎖的情況下訪問的根集對象。注意,MarkSweep類的成員函數(shù)MarkRoots只通過當前線程來標記根集對象。F.對于并行GC,由于標記階段并沒有掛起其它的ART運行時線程,因此這時候就調用成員函數(shù)MarkThreadRoots來并發(fā)標記那些不可以在沒有獲得Locks類的靜態(tài)成員變量mutator_lock_描述的讀寫鎖的情況下訪問的位于線程調用棧中的根集對象,接著再在當前線程中調用成員函數(shù)MarkNonThreadRoots標記那些不可以在沒有獲得Locks類的靜態(tài)成員變量mutator_lock_描述的讀寫鎖的情況下訪問的其它根集對象。G.獲得LiveStack的大小,保存成員變量live_stack_freeze_size_中。注意,這時候LiveStack的大小即為交換AllocationStack和LiveStack之前AllocationStack的大小,即從上次GC以來新分配的對象的個數(shù)。H.調用成員函數(shù)MarkConcurrentRoots標記那些可以在沒有獲得Locks類的靜態(tài)成員變量mutator_lock_描述的讀寫鎖的情況下訪問的根集對象。I.調用Heap類的成員函數(shù)UpdateAndMarkModUnion處理ModUnionTable中的DirtyCard。J.調用成員函數(shù)MarkReachableObjects遞歸標記那些可以從根集對象到達的其它對象。上述就是GC的標記階段,它是一個很復雜的過程,涉及到的關鍵函數(shù)有MarkSweep類的成員函數(shù)BindBitmap、MarkRoots、MarkThreadRoots、MarkNonThreadRoots、MarkConcurrentRoots和MarkReachableObjects,以及Heap類的成員函數(shù)ProcessCards、S和UpdateAndMarkModUnion。接下來我們就詳細分析這些函數(shù)的實現(xiàn),以及這些函數(shù)涉及到的概念。在前面一文中,我們提到,MarkSweep、PartialMarkSweep和StickyMarkSweep的回收范圍是不同的,它們分別通過實現(xiàn)的自己的成員函數(shù)BindBitmap來限定回收范圍,因此,接下來我們就分別分析MarkSweep、PartialMarkSweep和StickyMarkSweep三個類的成員函數(shù)BindBitmap的實現(xiàn)。MarkSweep類的成員函數(shù)BindBitmap的實現(xiàn)如下所示:[cpp]viewplaincopy在CODE上查看代碼片派生到我的代碼片voidMarkSweep::BindBitmaps(){timings_.StartSplit("BindBitmaps");WriterMutexLockmu(Thread::Current(),*Locks::heap_bitmap_lock_);//Markallofthespaceswenevercollectasimmune.for(constauto&space:GetHeap()->GetContinuousSpaces()){if(space->GetGcRetentionPolicy()==space::kGcRetentionPolicyNeverCollect){ImmuneSpace(space);}
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 二零二五年度嬰幼兒游泳館加盟服務合同4篇
- 二零二五年度實木地板翻新與保養(yǎng)服務合同4篇
- 2025年代理協(xié)議示范文本-辦公文具代理合同
- 2025版別墅區(qū)物業(yè)委托經(jīng)營管理服務標準范本3篇
- 二零二五年度公司股權激勵計劃后續(xù)管理與跟蹤合同2篇
- 2025年中國雙面羊絨大衣行業(yè)市場調研分析及投資戰(zhàn)略咨詢報告
- 2025年度海洋科學研究中心研究員聘用合同
- 2025年度交通行業(yè)短期運輸司機勞動合同
- 二零二五年度消防安全員消防技術咨詢服務聘用合同
- 二零二五年度農業(yè)科技推廣勞務合同執(zhí)行與效果評估
- 第三單元名著導讀《經(jīng)典常談》知識清單 統(tǒng)編版語文八年級下冊
- 第十七章-阿法芙·I·梅勒斯的轉變理論
- 焊接機器人在汽車制造中應用案例分析報告
- 合成生物學在生物技術中的應用
- 中醫(yī)門診病歷
- 廣西華銀鋁業(yè)財務分析報告
- 無違法犯罪記錄證明申請表(個人)
- 大學生勞動教育PPT完整全套教學課件
- 繼電保護原理應用及配置課件
- 《殺死一只知更鳥》讀書分享PPT
- 蓋洛普Q12解讀和實施完整版
評論
0/150
提交評論