<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [原文出處-------------ART運行時Foreground GC和Background GC切換過程分析](http://blog.csdn.net/luoshengyang/article/details/45301715) 通過前面一系列文章的學習,我們知道了ART運行時既支持Mark-Sweep GC,又支持Compacting GC。其中,Mark-Sweep GC執行效率更高,但是存在內存碎片問題;而Compacting GC執行效率較低,但是不存在內存碎片問題。ART運行時通過引入Foreground GC和Background GC的概念來對這兩種GC進行揚長避短。本文就詳細分析它們的執行過程以及切換過程。 在前面ART運行時Compacting GC簡要介紹和學習計劃和ART運行時Compacting GC堆創建過程分析這兩篇文章中,我們都有提到了ART運行時的Foreground GC和Background GC。它們是在ART運行時啟動通過-Xgc和-XX:BackgroundGC指定的。但是在某同一段時間,ART運行時只會執行Foreground GC或者Background GC。也就是說,Foreground GC和Background GC在整個應用程序的生命周期中是交替執行的。這就涉及到從Foreground GC切換到Background GC,或者從Background GC切換到Foreground GC的問題。 現在兩個問題就來了:什么時候執行Foreground GC,什么時候執行Background GC?什么GC作為Foreground GC最合適,什么GC作為Background GC最合適? 顧名思義,Foreground指的就是應用程序在前臺運行時,而Background就是應用程序在后臺運行時。因此,Foreground GC就是應用程序在前臺運行時執行的GC,而Background就是應用程序在后臺運行時執行的GC。 應用程序在前臺運行時,響應性是最重要的,因此也要求執行的GC是高效的。相反,應用程序在后臺運行時,響應性不是最重要的,這時候就適合用來解決堆的內存碎片問題。因此,Mark-Sweep GC適合作為Foreground GC,而Compacting GC適合作為Background GC。 但是,ART運行時又是怎么知道應用程序目前是運行在前臺還是后臺呢?這就需要負責管理應用程序組件的系統服務ActivityManagerService閃亮登場了。因為ActivityManagerService清楚地知道應用程序的每一個組件的運行狀態,也就是它們當前是在前臺運行還是后臺運行,從而得到應用程序是前臺運行還是后臺運行的結論。 我們通過圖1來描述應用程序的運行狀態與Foreground GC和Background GC的時序關系,如下所示: ![](https://box.kancloud.cn/648f9bc596d5c056db843ede8a7d667b_730x274.png) 圖1 應用程序運行狀態與Foreground GC和Background GC的時序關系 從圖1還可以看到,當從Foreground GC切換到Background GC,或者從Background GC切換到Foreground GC,會發生一次Compacting GC的行為。這是由于Foreground GC和Background GC的底層堆空間結構是一樣的,因此發生Foreground GC和Background GC切換時,需要將當前存活的對象從一個Space轉移到另外一個Space上去。這個剛好就是Semi-Space GC和Generational Semi-Space GC合適干的事情。 圖1中的顯示了應用程序的兩個狀態:kProcessStateJankPerceptible和kProcessStateJankImperceptible。其中,kProcessStateJankPerceptible說的就是應用程序處于用戶可感知的狀態,這就相當于是前臺狀態;而kProcessStateJankImperceptible說的就是應用程序處于用戶不可感知的狀態,這就相當于是后臺狀態。 接下來,我們就結合ActivityManagerService來分析Foreground GC和Background GC的切換過程。 從前面[Android應用程序的Activity啟動過程簡要介紹和學習計劃](http://blog.csdn.net/luoshengyang/article/details/6685853)這個系列的文章可以知道,應用程序組件是通過ActivityManagerService進行啟動的。例如,當我們從Launcher啟動一個應用程序時,實際的是在這個應用程序中Action和Category分別被配置為MAIN和LAUNCHER的Activity。這個Activity最終由ActivityManagerService通知其所在的進程進行啟動工作的,也就是通過ApplicationThread類的成員函數scheduleLaunchActivity開始執行啟動工作的。其它類型的組件的啟動過程也是類似的,這里我們僅以Activity的啟動過程作為示例,來說明ART運行時如何知道要進行Foreground GC和Background GC切換的。 ApplicationThread類的成員函數scheduleLaunchActivity的實現如下所示: ~~~ public final class ActivityThread { ...... private class ApplicationThread extends ApplicationThreadNative { ...... public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { updateProcessState(procState, false); ActivityClientRecord r = new ActivityClientRecord(); r.token = token; r.ident = ident; r.intent = intent; r.voiceInteractor = voiceInteractor; r.activityInfo = info; r.compatInfo = compatInfo; r.state = state; r.persistentState = persistentState; r.pendingResults = pendingResults; r.pendingIntents = pendingNewIntents; r.startsNotResumed = notResumed; r.isForward = isForward; r.profilerInfo = profilerInfo; updatePendingConfiguration(curConfig); sendMessage(H.LAUNCH_ACTIVITY, r); } ...... } ...... } ~~~ 這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java中。 ApplicationThread類的成員函數scheduleLaunchActivity首先是調用另外一個成員函數updateProcessState更新進程的當前狀態,接著再將其余參數封裝在一個ActivityClientRecord對象中,并且將這個ActivityClientRecord對象通過一個H.LAUNCH_ACTIVITY消息傳遞給應用程序主線程處理。應用程序主線程處理對這個消息的處理就是啟動指定的Activity,這個過程可以參考前面[Android應用程序的Activity啟動過程簡要介紹和學習計劃](http://blog.csdn.net/luoshengyang/article/details/6685853)這個系列的文章。ApplicationThread類的成員函數scheduleLaunchActivity還調用了另外一個成員函數updatePendingConfiguration將參數curConfig描述的系統當前配置信息保存下來待后面處理。 我們主要關注ApplicationThread類的成員函數updateProcessState,因為它涉及到進程狀態的更新,它的實現如下所示: ~~~ public final class ActivityThread { ...... private class ApplicationThread extends ApplicationThreadNative { ...... public void updateProcessState(int processState, boolean fromIpc) { synchronized (this) { if (mLastProcessState != processState) { mLastProcessState = processState; // Update Dalvik state based on ActivityManager.PROCESS_STATE_* constants. final int DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE = 0; final int DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1; int dalvikProcessState = DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE; // TODO: Tune this since things like gmail sync are important background but not jank perceptible. if (processState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { dalvikProcessState = DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE; } VMRuntime.getRuntime().updateProcessState(dalvikProcessState); ...... } } } ...... } ...... } ~~~ 這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java中。 ApplicationThread類的成員變量mLastProcessState描述的是進程上一次的狀態,而參數processState描述的是進程當前的狀態。當這兩者的值不一致時,就表明進程的狀態發生了變化,這時候就需要調用VMRuntime類的成員函數updateProcessState通知ART運行時,以便ART運行時可以在Foreground GC和Background GC之間切換。 ActivityManagerService一共定義了14種進程狀態,如下所示: ~~~ public class ActivityManager { ...... /** @hide Process is a persistent system process. */ public static final int PROCESS_STATE_PERSISTENT = 0; /** @hide Process is a persistent system process and is doing UI. */ public static final int PROCESS_STATE_PERSISTENT_UI = 1; /** @hide Process is hosting the current top activities. Note that this covers * all activities that are visible to the user. */ public static final int PROCESS_STATE_TOP = 2; /** @hide Process is important to the user, and something they are aware of. */ public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 3; /** @hide Process is important to the user, but not something they are aware of. */ public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 4; /** @hide Process is in the background running a backup/restore operation. */ public static final int PROCESS_STATE_BACKUP = 5; /** @hide Process is in the background, but it can't restore its state so we want * to try to avoid killing it. */ public static final int PROCESS_STATE_HEAVY_WEIGHT = 6; /** @hide Process is in the background running a service. Unlike oom_adj, this level * is used for both the normal running in background state and the executing * operations state. */ public static final int PROCESS_STATE_SERVICE = 7; /** @hide Process is in the background running a receiver. Note that from the * perspective of oom_adj receivers run at a higher foreground level, but for our * prioritization here that is not necessary and putting them below services means * many fewer changes in some process states as they receive broadcasts. */ public static final int PROCESS_STATE_RECEIVER = 8; /** @hide Process is in the background but hosts the home activity. */ public static final int PROCESS_STATE_HOME = 9; /** @hide Process is in the background but hosts the last shown activity. */ public static final int PROCESS_STATE_LAST_ACTIVITY = 10; /** @hide Process is being cached for later use and contains activities. */ public static final int PROCESS_STATE_CACHED_ACTIVITY = 11; /** @hide Process is being cached for later use and is a client of another cached * process that contains activities. */ public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12; /** @hide Process is being cached for later use and is empty. */ public static final int PROCESS_STATE_CACHED_EMPTY = 13; ...... } ~~~ 這些進程狀態值定義在文件frameworks/base/core/java/android/app/ActivityManager.java。 每一個進程狀態都通過一個整數來描述,其中,值越小就表示進程越重要。ART運行時將狀態值大于等于PROCESS_STATE_IMPORTANT_FOREGROUND的進程都認為是用戶可感知的,也就是前臺進程,其余的進程則認為是用戶不可感知的,也就是后臺進程。通過這種方式,ApplicationThread類的成員函數updateProcessState就可以簡化ART運行時對進程狀態的處理。 除了上述的Activity的Launch啟動生命周期函數被ActivityManagerService通知調用時,Activity的Resume生命周期函數被ActivityManagerService通知調用調用時,也會發生類似的通過VMRuntime類的成員函數updateProcessState通知ART運行時應用程序狀態發生了改變。對于其它的組件,例如Broadcast Receiver組件被觸發時,Service組件被創建以及被綁定時,也會通過VMRuntime類的成員函數updateProcessState通知ART運行時應用程序狀態發生了改變。 不過,上述組件的生命周期對應的都是應用程序處于前臺時的情況,也就是要求ART運行時從Background GC切換為Foreground GC的情況。當應用程序處于后臺時,ActivityManagerService是通過直接設置應用程序的狀態來通知ART運行時應用程序狀態發生了改變的。 ApplicationThread類實現了一個Binder接口setProcessState,供ActivityManagerService直接設置應用程序的狀態,它的實現如下所示: ~~~ public final class ActivityThread { ...... private class ApplicationThread extends ApplicationThreadNative { ...... public void setProcessState(int state) { updateProcessState(state, true); } ...... } ...... } ~~~ 這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java中。 ApplicationThread類實現的Binder接口setProcessState也是通過上面分析的成員函數updateProcessState來通知ART運行時進程狀態發生了改變的。不過這時候進程的狀態就有可能是從前面進程變為后臺進程,例如當運行在該進程的Activity組件處理Stop狀態時。 接下來我們繼續分析VMRuntime類的成員函數updateProcessState的實現,以便了解ART運行時執行Foreground GC和Background GC切換的過程,如下所示: ~~~ public final class VMRuntime { ...... /** * Let the heap know of the new process state. This can change allocation and garbage collection * behavior regarding trimming and compaction. */ public native void updateProcessState(int state); ...... } ~~~ 這個函數定義在文件libcore/libart/src/main/java/dalvik/system/VMRuntime.java中。 VMRuntime類的成員函數updateProcessState是一個Native函數,它由C++層的函數VMRuntime_updateProcessState實現,如下所示: ~~~ static void VMRuntime_updateProcessState(JNIEnv* env, jobject, jint process_state) { Runtime::Current()->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(process_state)); ...... } ~~~ 這個函數定義在文件art/runtime/native/dalvik_system_VMRuntime.cc中。 函數VMRuntime_updateProcessState主要是調用了Heap類的成員函數UpdateProcessState來通知ART運行時切換Foreground GC和Background GC,后者的實現如下所示: ~~~ void Heap::UpdateProcessState(ProcessState process_state) { if (process_state_ != process_state) { process_state_ = process_state; ...... if (process_state_ == kProcessStateJankPerceptible) { // Transition back to foreground right away to prevent jank. RequestCollectorTransition(foreground_collector_type_, 0); } else { // Don't delay for debug builds since we may want to stress test the GC. // If background_collector_type_ is kCollectorTypeHomogeneousSpaceCompact then we have // special handling which does a homogenous space compaction once but then doesn't transition // the collector. RequestCollectorTransition(background_collector_type_, kIsDebugBuild ? 0 : kCollectorTransitionWait); } } } ~~~ 這個函數定義在文件art/runtime/gc/heap.cc中。 Heap類的成員變量process_state_記錄了進程上一次的狀態,參數process_state描述進程當前的狀態。當這兩者的值不相等的時候,就說明進程狀態發生了變化。 如果是從kProcessStateJankImperceptible狀態變為kProcessStateJankPerceptible狀態,那么就調用Heap類的成員函數RequestCollectorTransition請求馬上將當前的GC設置為Foreground GC。 如果是從kProcessStateJankPerceptible狀態變為kProcessStateJankImperceptible,那么就調用Heap類的成員函數RequestCollectorTransition請求將當前的GC設置為Background GC。注意,在這種情況下,對于非DEBUG版本的ART運行時,不是馬上將當前的GC設置為Background GC的,而是指定在kCollectorTransitionWait(5秒)時間后再設置。這樣使得進程進入后臺運行的一小段時間內,仍然可以使用效率較高的Mark-Sweep GC。 Heap類的成員函數RequestCollectorTransition的實現如下所示: ~~~ void Heap::RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time) { Thread* self = Thread::Current(); { MutexLock mu(self, *heap_trim_request_lock_); if (desired_collector_type_ == desired_collector_type) { return; } heap_transition_or_trim_target_time_ = std::max(heap_transition_or_trim_target_time_, NanoTime() + delta_time); desired_collector_type_ = desired_collector_type; } SignalHeapTrimDaemon(self); } ~~~ 這個函數定義在文件art/runtime/gc/heap.cc中。 Heap類的成員函數RequestCollectorTransition首先將要切換至的目標GC以及時間點記錄在成員變量desired_collector_type_和heap_transition_or_trim_target_time_中,接著再調用另外一個成員函數SignalHeapTrimDaemon喚醒一個Heap Trimmer守護線程來執行GC切換操作。注意,如果上一次請求的GC切換還未執行,又請求了下一次GC切換,并且下一次GC切換指定的時間大于上一次指定的時間,那么上次請求的GC切換就會被取消。 Heap類的成員函數RequestCollectorTransition的實現如下所示: ~~~ void Heap::SignalHeapTrimDaemon(Thread* self) { JNIEnv* env = self->GetJniEnv(); DCHECK(WellKnownClasses::java_lang_Daemons != nullptr); DCHECK(WellKnownClasses::java_lang_Daemons_requestHeapTrim != nullptr); env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons, WellKnownClasses::java_lang_Daemons_requestHeapTrim); CHECK(!env->ExceptionCheck()); } ~~~ 這個函數定義在文件art/runtime/gc/heap.cc中。 Heap類的成員函數RequestCollectorTransition通過JNI接口調用了Daemons類的靜態成員函數requestHeapTrim請求執行一次GC切換操作。 Daemons類的靜態成員函數requestHeapTrim的實現如下所示: ~~~ public final class Daemons { ...... public static void requestHeapTrim() { synchronized (HeapTrimmerDaemon.INSTANCE) { HeapTrimmerDaemon.INSTANCE.notify(); } } ...... } ~~~ 這個函數定義在文件libcore/libart/src/main/java/java/lang/Daemons.java中。 在前面[ART運行時垃圾收集(GC)過程分析](http://blog.csdn.net/luoshengyang/article/details/42555483)一文中提到,Java層的java.lang.Daemons類在加載的時候,會啟動五個與堆或者GC相關的守護線程,其中一個守護線程就是HeapTrimmerDaemon,這里通過調用它的成員函數notify來喚醒它。 HeapTrimmerDaemon原先被Blocked在成員函數run中,當它被喚醒之后 ,就會繼續執行它的成員函數run,如下所示: ~~~ public final class Daemons { ...... private static class HeapTrimmerDaemon extends Daemon { private static final HeapTrimmerDaemon INSTANCE = new HeapTrimmerDaemon(); @Override public void run() { while (isRunning()) { try { synchronized (this) { wait(); } VMRuntime.getRuntime().trimHeap(); } catch (InterruptedException ignored) { } } } } ...... } ~~~ 這個函數定義在文件libcore/libart/src/main/java/java/lang/Daemons.java中。 從這里就可以看到,HeapTrimmerDaemon被喚醒之后,就會調用VMRuntime類的成員函數trimHeap來執行GC切換操作。 VMRuntime類的成員函數trimHeap是一個Native函數,由C++層的函數VMRuntime_trimHeap實現,如下所示: ~~~ static void VMRuntime_trimHeap(JNIEnv*, jobject) { Runtime::Current()->GetHeap()->DoPendingTransitionOrTrim(); } ~~~ 這個函數定義在文件art/runtime/native/dalvik_system_VMRuntime.cc 。 函數VMRuntime_trimHeap又是通過調用Heap類的成員函數DoPendingTransitionOrTrim來執行GC切換操作的,如下所示: ~~~ void Heap::DoPendingTransitionOrTrim() { Thread* self = Thread::Current(); CollectorType desired_collector_type; // Wait until we reach the desired transition time. while (true) { uint64_t wait_time; { MutexLock mu(self, *heap_trim_request_lock_); desired_collector_type = desired_collector_type_; uint64_t current_time = NanoTime(); if (current_time >= heap_transition_or_trim_target_time_) { break; } wait_time = heap_transition_or_trim_target_time_ - current_time; } ScopedThreadStateChange tsc(self, kSleeping); usleep(wait_time / 1000); // Usleep takes microseconds. } // Launch homogeneous space compaction if it is desired. if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) { if (!CareAboutPauseTimes()) { PerformHomogeneousSpaceCompact(); } // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory. desired_collector_type = collector_type_; return; } // Transition the collector if the desired collector type is not the same as the current // collector type. TransitionCollector(desired_collector_type); ...... // Do a heap trim if it is needed. Trim(); } ~~~ 這個函數定義在文件art/runtime/gc/heap.cc中。 前面提到,下一次GC切換時間記錄在Heap類的成員變量heap_transition_or_trim_target_time_中,因此,Heap類的成員函數DoPendingTransitionOrTrim首先是看看當前時間是否已經達到指定的GC切換時間。如果還沒有達到,那么就進行等待,直到時間到達為止。 有一種特殊情況,如果要切換至的GC是kCollectorTypeHomogeneousSpaceCompact,并且Heap類的成員函數CareAboutPauseTimes表明不在乎執行HomogeneousSpaceCompact GC帶來的暫停時間,那么就會調用Heap類的成員函數PerformHomogeneousSpaceCompact執行一次同構空間壓縮。Heap類的成員函數PerformHomogeneousSpaceCompact執行同構空間壓縮的過程,可以參考前面[ART運行時Compacting GC為新創建對象分配內存的過程分析](http://blog.csdn.net/luoshengyang/article/details/44910271)一文。 Heap類的成員函數CareAboutPauseTimes實際上是判斷進程的當前狀態是否是用戶可感知的,即是否等于kProcessStateJankPerceptible。如果是的話,就說明它在乎GC執行時帶來的暫停時間。它的實現如下所示: ~~~ class Heap { public: ...... // Returns true if we currently care about pause times. bool CareAboutPauseTimes() const { return process_state_ == kProcessStateJankPerceptible; } ...... }; ~~~ 這個函數定義在文件art/runtime/gc/heap.h中。 回到Heap類的成員函數DoPendingTransitionOrTrim中,我們繼續討論要切換至的GC是kCollectorTypeHomogeneousSpaceCompact的情況。如果Heap類的成員函數CareAboutPauseTimes表明在乎執行HomogeneousSpaceCompact GC帶來的暫停時間,那么就不會調用Heap類的成員函數PerformHomogeneousSpaceCompact執行同構空間壓縮。 只要切換至的GC是kCollectorTypeHomogeneousSpaceCompact,無論上述的哪一種情況,都不會真正執行GC切換的操作,因此這時候Heap類的成員函數DoPendingTransitionOrTrim就可以返回了。 從前面的調用過程可以知道,要切換至的GC要么是Foreground GC,要么是Background GC。一般來說,我們是不會將Foreground GC設置為HomogeneousSpaceCompact GC的,但是卻有可能將Background GC設置為HomogeneousSpaceCompact GC。因此,上述討論的情況只發生在Foreground GC切換為Background GC的時候。 另一方面,如果要切換至的GC不是kCollectorTypeHomogeneousSpaceCompact,那么Heap類的成員函數DoPendingTransitionOrTrim就會調用另外一個成員函數TransitionCollector執行切換GC操作。一旦GC切換完畢,Heap類的成員函數DoPendingTransitionOrTrim還會調用成員函數Trim對當前ART運行時堆進行裁剪,也就是將現在沒有使用到的內存歸還給內核。這個過程可以參考前面[ART運行時垃圾收集(GC)過程分析](http://blog.csdn.net/luoshengyang/article/details/42555483)一文。 接下來我們繼續分析Heap類的成員函數TransitionCollector的實現,以便了解GC的切換過程,如下所示: ~~~ void Heap::TransitionCollector(CollectorType collector_type) { if (collector_type == collector_type_) { return; } ...... ThreadList* const tl = runtime->GetThreadList(); ...... // Busy wait until we can GC (StartGC can fail if we have a non-zero // compacting_gc_disable_count_, this should rarely occurs). for (;;) { { ScopedThreadStateChange tsc(self, kWaitingForGcToComplete); MutexLock mu(self, *gc_complete_lock_); // Ensure there is only one GC at a time. WaitForGcToCompleteLocked(kGcCauseCollectorTransition, self); // Currently we only need a heap transition if we switch from a moving collector to a // non-moving one, or visa versa. const bool copying_transition = IsMovingGc(collector_type_) != IsMovingGc(collector_type); // If someone else beat us to it and changed the collector before we could, exit. // This is safe to do before the suspend all since we set the collector_type_running_ before // we exit the loop. If another thread attempts to do the heap transition before we exit, // then it would get blocked on WaitForGcToCompleteLocked. if (collector_type == collector_type_) { return; } // GC can be disabled if someone has a used GetPrimitiveArrayCritical but not yet released. if (!copying_transition || disable_moving_gc_count_ == 0) { // TODO: Not hard code in semi-space collector? collector_type_running_ = copying_transition ? kCollectorTypeSS : collector_type; break; } } usleep(1000); } tl->SuspendAll(); switch (collector_type) { case kCollectorTypeSS: { if (!IsMovingGc(collector_type_)) { // Create the bump pointer space from the backup space. ...... std::unique_ptr<MemMap> mem_map(main_space_backup_->ReleaseMemMap()); // We are transitioning from non moving GC -> moving GC, since we copied from the bump // pointer space last transition it will be protected. ..... mem_map->Protect(PROT_READ | PROT_WRITE); bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space", mem_map.release()); AddSpace(bump_pointer_space_); Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition); // Use the now empty main space mem map for the bump pointer temp space. mem_map.reset(main_space_->ReleaseMemMap()); // Unset the pointers just in case. if (dlmalloc_space_ == main_space_) { dlmalloc_space_ = nullptr; } else if (rosalloc_space_ == main_space_) { rosalloc_space_ = nullptr; } // Remove the main space so that we don't try to trim it, this doens't work for debug // builds since RosAlloc attempts to read the magic number from a protected page. RemoveSpace(main_space_); RemoveRememberedSet(main_space_); delete main_space_; // Delete the space since it has been removed. main_space_ = nullptr; RemoveRememberedSet(main_space_backup_.get()); main_space_backup_.reset(nullptr); // Deletes the space. temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2", mem_map.release()); AddSpace(temp_space_); } break; } case kCollectorTypeMS: // Fall through. case kCollectorTypeCMS: { if (IsMovingGc(collector_type_)) { ...... std::unique_ptr<MemMap> mem_map(temp_space_->ReleaseMemMap()); RemoveSpace(temp_space_); temp_space_ = nullptr; mem_map->Protect(PROT_READ | PROT_WRITE); CreateMainMallocSpace(mem_map.get(), kDefaultInitialSize, mem_map->Size(), mem_map->Size()); mem_map.release(); // Compact to the main space from the bump pointer space, don't need to swap semispaces. AddSpace(main_space_); Compact(main_space_, bump_pointer_space_, kGcCauseCollectorTransition); mem_map.reset(bump_pointer_space_->ReleaseMemMap()); RemoveSpace(bump_pointer_space_); bump_pointer_space_ = nullptr; const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1]; ...... main_space_backup_.reset(CreateMallocSpaceFromMemMap(mem_map.get(), kDefaultInitialSize, mem_map->Size(), mem_map->Size(), name, true)); ...... mem_map.release(); } break; } default: { ...... break; } ChangeCollector(collector_type); tl->ResumeAll(); ...... } ~~~ 這個函數定義在文件art/runtime/gc/heap.h中。 Heap類的成員函數TransitionCollector首先判斷ART運行時當前使用的GC與要切換至的GC是一樣的,那么就什么也不用做就返回了。 另一方面,如果ART運行時當前使用的GC與要切換至的GC是不一樣的,那么接下來就要將ART運行時當前使用的GC切換至參數collector_type描述的GC了。由于將GC切換是通過執行一次Semi-Space GC或者Generational Semi-Space GC來實現的,因此Heap類的成員函數TransitionCollector在繼續往下執行之前,要先調用Heap類的成員函數WaitForGcToCompleteLocked判斷當前是否有GC正在執行。如果有的話,就進行等待,直到對應的GC執行完為止。 注意,有可能當前正在執行的GC就是要切換至的GC,在這種情況下,就沒有必要將當前使用的GC切換為參數collector_type描述的GC了。此外,只有從當前執行的GC和要切換至的GC不同時為Compacting GC或者Mark-Sweep GC的時候,Heap類的成員函數TransitionCollector才會真正執行切換的操作。換句話說,只有從Compacting GC切換為Mark-Sweep GC或者從Mark-Sweep GC切換為Compacting GC時,Heap類的成員函數TransitionCollector才會真正執行切換的操作。但是,如果這時候ART運行時被禁止執行Compacting GC,即Heap類的成員函數disable_moving_gc_count_不等于0,那么Heap類的成員函數TransitionCollector就需要繼續等待,直到ART運行時重新允許執行Compacting GC為止。這是因為接下來的GC切換操作是通過執行一次Compacting GC來實現的。 接下來的GC切換操作是通過調用Heap類的成員函數Compact來實現的。關于Heap類的成員函數Compact,我們在前面[ART運行時Compacting GC為新創建對象分配內存的過程分析](http://blog.csdn.net/luoshengyang/article/details/44910271)一文已經分析過了,它主要通過執行一次Semi-Space GC、Generational Semi-Space GC或者Mark-Compact GC將指定的Source Space上的存活對象移動至指定的Target Space中。如果Source Space與Target Space相同,那么執行的就是Mark-Compact GC,否則就是Semi-Space GC或者Generational Semi-Space GC。由于Heap類的成員函數Compact是需要在Stop-the-world的前提下執行的,因此在調用它的前后,需要執行掛起和恢復除當前正在執行的線程之外的所有ART運行時線程。 Heap類的成員函數TransitionCollector通過switch語句來確定需要傳遞給成員函數Compact的Source Space和Target Space。通過這個switch語句,我們也可以更清楚看到Heap類的成員函數TransitionCollector允許從什么GC切換至什么GC。 首先,可切換至的GC只有三種,分別為Semi-Space GC、Mark-Sweep GC和Concurrent Mark-Sweep GC。其中,當要切換至的GC為Mark-Sweep GC和Concurrent Mark-Sweep GC時,它們的切換過程是一樣的。因此,接下來我們就分兩種情況來討論。 第一種情況是要切換至的GC為Semi-Space GC。根據我們前面的分析,這時候原來的GC只能為Mark-Sweep GC或者Concurrent Mark-Sweep GC。否則的話,就不需要執行GC切換操作。從前面[ART運行時Compacting GC堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/44789295)一文可以知道,當原來的GC為Mark-Sweep GC或者Concurrent Mark-Sweep GC時,ART運行時堆由Image Space、Zygote Space、Non Moving Space、Main Space、Main Backup Space和Large Object Space組成。這時候要做的是將Main Space上的存活對象移動至一個新創建的Bump Pointer Space上去。也就是說,這時候的Source Space為Main Space,而Target Space為Bump Pointer Space。 Main Space就保存在Heap類的成員變量main_space_中,因此就很容易可以獲得。但是這時候是沒有現成的Bump Pointer Space的,因此就需要創建一個。由于這時候的Main Backup Space是閑置的,并且當GC切換完畢,它也用不上了,因此我們就可以將Main Backup Space底層使用的內存塊獲取回來,然后再封裝成一個Bump Pointer Space。注意,這時候創建的Bump Pointer Space也是作為GC切換完成后的Semi-Space GC的From Space使用的,因此,除了要將它保存在Heap類的成員變量bump_pointer_space_之外,還要將它添加到ART運行時堆的Space列表中去。 這時候Source Space和Target Space均已準備完畢,因此就可以執行Heap類的成員函數Compact了。執行完畢,還需要做一系列的清理工作,包括: 1. 刪除Main Space及其關聯的Remembered Set。從前面[ART運行時Compacting GC堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/44789295)一文可以知道,Heap類的成員變量dlmalloc_space_和rosalloc_space_指向的都是Main Space。既然現在Main Space要被刪除了,因此就需要將它們設置為nullptr。 2. 刪除Main Backup Space及其關聯的Remembered Set。 3. 創建一個Bump Pointer Space保存在Heap類的成員變量temp_space_中,作為GC切換完成后的Semi-Space GC的To Space使用。注意,這個To Space底層使用的內存塊是來自于原來的Main Space的。 這意味著將從Mark-Sweep GC或者Concurrent Mark-Sweep GC切換為Semi-Space GC之后,原來的Main Space和Main Backup Space就消失了,并且多了兩個Bump Pointer Space,其中一個作為From Space,另外一個作為To Space,并且From Space上的對象來自于原來的Main Space的存活對象。 第二種情況是要切換至Mark-Sweep GC或者Concurrent Mark-Sweep GC。根據我們前面的分析,這時候原來的GC只能為Semi-Space GC、Generational Semi-Space GC或者Mark-Compact GC。否則的話,就不需要執行GC切換操作。從前面[ART運行時Compacting GC堆創建過程分析](http://blog.csdn.net/luoshengyang/article/details/44789295)一文可以知道,當原來的GC為Semi-Space GC、Generational Semi-Space GC或者Mark-Compact GC時,ART運行時堆由Image Space、Zygote Space、Non Moving Space、Bump Pointer Space、Temp Space和Large Object Space組成。這時候要做的是將Bump Pointer Space上的存活對象移動至一個新創建的Main Space上去。也就是說,這時候的Source Space為Bump Pointer Space,而Target Space為Main Space。 Bump Pointer Space就保存在Heap類的成員變量bump_pointer_space_中,因此就很容易可以獲得。但是這時候是沒有現成的Main Space的,因此就需要創建一個。由于這時候的Temp Space是閑置的,并且當GC切換完畢,它也用不上了,因此我們就可以將Temp Space底層使用的內存塊獲取回來,然后再封裝成一個Main Space,這是通過調用Heap類的成員函數CreateMainMallocSpace來實現的。注意,Heap類的成員函數CreateMainMallocSpace在執行的過程中,會將創建的Main Space保存在Heap類的成員變量main_space_中,并且它也是作為GC切換完成后的Mark-Sweep GC或者Concurrent Mark-Sweep GC的Main Space使用的,因此,就還要將它添加到ART運行時堆的Space列表中去。 這時候Source Space和Target Space均已準備完畢,因此就可以執行Heap類的成員函數Compact了。執行完畢,還需要做一系列的清理工作,包括: 1. 刪除Bump Pointer Space。 2. 刪除Temp Space。 3. 創建一個Main Backup Space,保存在Heap類的成員變量main_space_backup_中,這是通過調用Heap類的成員函數CreateMallocSpaceFromMemMap實現的,并且該Main Backup Space底層使用的內存塊是來自于原來的Bump Pointer Space的。 這樣,GC切換的操作就基本執行完畢,最后還需要做的一件事情是調用Heap類的成員函數ChangeCollector記錄當前使用的GC,以及相應地調整當前可用的內存分配器。這個函數的具體實現可以參考前面[ART運行時Compacting GC為新創建對象分配內存的過程分析](http://blog.csdn.net/luoshengyang/article/details/44910271)一文。 至此,ART運行時Foreground GC和Background GC的切換過程分析就分析完成了,ART運行時引進的Compacting GC的學習計劃也完成了,重新學習可以參考ART運行時Compacting GC簡要介紹和學習計劃一文
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看