通過前面一系列文章的學習,我們知道了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的時序關系,如下所示:
圖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啟動過程簡要介紹和學習計劃這個系列的文章可以知道,應用程序組件是通過ActivityManagerService進行啟動的。例如,當我們從Launcher啟動一個應用程序時,實際的是在這個應用程序中Action和Category分別被配置為MAIN和LAUNCHER的Activity。這個Activity終由ActivityManagerService通知其所在的進程進行啟動工作的,也就是通過ApplicationThread類的成員函數scheduleLaunchActivity開始執行啟動工作的。其它類型的組件的啟動過程也是類似的,這里我們僅以Activity的啟動過程作為示例,來說明ART運行時如何知道要進行Foreground GC和Background GC切換的。
ApplicationThread類的成員函數scheduleLaunchActivity的實現如下所示:
[java] view plaincopy
1public final class ActivityThread {
2 ......
3
4 private class ApplicationThread extends ApplicationThreadNative {
5 ......
6
7 public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
8 ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
9 IVoiceInteractor voiceInteractor, int procState, Bundle state,
10 PersistableBundle persistentState, List<ResultInfo> pendingResults,
11 List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
12 ProfilerInfo profilerInfo) {
13
14 updateProcessState(procState, false);
15
16 ActivityClientRecord r = new ActivityClientRecord();
17
18 r.token = token;
19 r.ident = ident;
20 r.intent = intent;
21 r.voiceInteractor = voiceInteractor;
22 r.activityInfo = info;
23 r.compatInfo = compatInfo;
24 r.state = state;
25 r.persistentState = persistentState;
26
27 r.pendingResults = pendingResults;
28 r.pendingIntents = pendingNewIntents;
29
30 r.startsNotResumed = notResumed;
31 r.isForward = isForward;
32
33 r.profilerInfo = profilerInfo;
34
35 updatePendingConfiguration(curConfig);
36
37 sendMessage(H.LAUNCH_ACTIVITY, r);
38 }
39
40 ......
41 }
52
43 ......
44}
這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java中。
ApplicationThread類的成員函數scheduleLaunchActivity首先是調用另外一個成員函數updateProcessState更新進程的當前狀態,接著再將其余參數封裝在一個ActivityClientRecord對象中,并且將這個ActivityClientRecord對象通過一個H.LAUNCH_ACTIVITY消息傳遞給應用程序主線程處理。應用程序主線程處理對這個消息的處理就是啟動指定的Activity,這個過程可以參考前面Android應用程序的Activity啟動過程簡要介紹和學習計劃這個系列的文章。ApplicationThread類的成員函數scheduleLaunchActivity還調用了另外一個成員函數updatePendingConfiguration將參數curConfig描述的系統當前配置信息保存下來待后面處理。
我們主要關注ApplicationThread類的成員函數updateProcessState,因為它涉及到進程狀態的更新,它的實現如下所示:
[java] view plaincopy
1 public final class ActivityThread {
2 ......
3
4 private class ApplicationThread extends ApplicationThreadNative {
5 ......
6
7 public void updateProcessState(int processState, boolean fromIpc) {
8 synchronized (this) {
9 if (mLastProcessState != processState) {
10 mLastProcessState = processState;
11 // Update Dalvik state based on ActivityManager.PROCESS_STATE_* constants.
12 final int DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE = 0;
13 final int DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE = 1;
14 int dalvikProcessState = DALVIK_PROCESS_STATE_JANK_IMPERCEPTIBLE;
15 // TODO: Tune this since things like gmail sync are important background but not jank perceptible.
16 if (processState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
17 dalvikProcessState = DALVIK_PROCESS_STATE_JANK_PERCEPTIBLE;
18 }
19 VMRuntime.getRuntime().updateProcessState(dalvikProcessState);
20 ......
21 }
22 }
23 }
24
25 ......
26 }
27
28 ......
29}
這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java中。
ApplicationThread類的成員變量mLastProcessState描述的是進程上一次的狀態,而參數processState描述的是進程當前的狀態。當這兩者的值不一致時,就表明進程的狀態發生了變化,這時候就需要調用VMRuntime類的成員函數updateProcessState通知ART運行時,以便ART運行時可以在Foreground GC和Background GC之間切換。
ActivityManagerService一共定義了14種進程狀態,如下所示:
[java] view plaincopy
1public class ActivityManager {
2 ......
3
4 /** @hide Process is a persistent system process. */
5 public static final int PROCESS_STATE_PERSISTENT = 0;
6
7 /** @hide Process is a persistent system process and is doing UI. */
8 public static final int PROCESS_STATE_PERSISTENT_UI = 1;
9
10 /** @hide Process is hosting the current top activities. Note that this covers
11 * all activities that are visible to the user. */
12 public static final int PROCESS_STATE_TOP = 2;
13
14 /** @hide Process is important to the user, and something they are aware of. */
15 public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 3;
16
17 /** @hide Process is important to the user, but not something they are aware of. */
18 public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 4;
19
20 /** @hide Process is in the background running a backup/restore operation. */
21 public static final int PROCESS_STATE_BACKUP = 5;
22
23 /** @hide Process is in the background, but it can't restore its state so we want
24 * to try to avoid killing it. */
25 public static final int PROCESS_STATE_HEAVY_WEIGHT = 6;
26
27 /** @hide Process is in the background running a service. Unlike oom_adj, this level
28 * is used for both the normal running in background state and the executing
29 * operations state. */
30 public static final int PROCESS_STATE_SERVICE = 7;
31
32 /** @hide Process is in the background running a receiver. Note that from the
33 * perspective of oom_adj receivers run at a higher foreground level, but for our
34 * prioritization here that is not necessary and putting them below services means
35 * many fewer changes in some process states as they receive broadcasts. */
36 public static final int PROCESS_STATE_RECEIVER = 8;
37
38 /** @hide Process is in the background but hosts the home activity. */
39 public static final int PROCESS_STATE_HOME = 9;
40
41 /** @hide Process is in the background but hosts the last shown activity. */
42 public static final int PROCESS_STATE_LAST_ACTIVITY = 10;
43
44 /** @hide Process is being cached for later use and contains activities. */
45 public static final int PROCESS_STATE_CACHED_ACTIVITY = 11;
46
47 /** @hide Process is being cached for later use and is a client of another cached
48 * process that contains activities. */
49 public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12;
50
51 /** @hide Process is being cached for later use and is empty. */
52 public static final int PROCESS_STATE_CACHED_EMPTY = 13;
53
54 ......
55}
這些進程狀態值定義在文件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直接設置應用程序的狀態,它的實現如下所示:
[java] view plaincopy
1public final class ActivityThread {
2 ......
3
4 private class ApplicationThread extends ApplicationThreadNative {
5 ......
6
7 public void setProcessState(int state) {
8 updateProcessState(state, true);
9 }
10
11 ......
12 }
13
14 ......
15}
這個函數定義在文件frameworks/base/core/java/android/app/ActivityThread.java中。
ApplicationThread類實現的Binder接口setProcessState也是通過上面分析的成員函數updateProcessState來通知ART運行時進程狀態發生了改變的。不過這時候進程的狀態就有可能是從前面進程變為后臺進程,例如當運行在該進程的Activity組件處理Stop狀態時。
接下來我們繼續分析VMRuntime類的成員函數updateProcessState的實現,以便了解ART運行時執行Foreground GC和Background GC切換的過程,如下所示:
[java] view plaincopy
1public final class VMRuntime {
2 ......
3
4 /**
5 * Let the heap know of the new process state. This can change allocation and garbage collection
6 * behavior regarding trimming and compaction.
7 */
8 public native void updateProcessState(int state);
9
10 ......
11}
這個函數定義在文件libcore/libart/src/main/java/dalvik/system/VMRuntime.java中。
VMRuntime類的成員函數updateProcessState是一個Native函數,它由C++層的函數VMRuntime_updateProcessState實現,如下所示:
[cpp] view plaincopy
1static void VMRuntime_updateProcessState(JNIEnv* env, jobject, jint process_state) {
2 Runtime::Current()->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(process_state));
3 ......
4}
這個函數定義在文件art/runtime/native/dalvik_system_VMRuntime.cc中。
函數VMRuntime_updateProcessState主要是調用了Heap類的成員函數UpdateProcessState來通知ART運行時切換Foreground GC和Background GC,后者的實現如下所示:
[cpp] view plaincopy
1void Heap::UpdateProcessState(ProcessState process_state) {
2 if (process_state_ != process_state) {
3 process_state_ = process_state;
4 ......
5 if (process_state_ == kProcessStateJankPerceptible) {
6 // Transition back to foreground right away to prevent jank.
7 RequestCollectorTransition(foreground_collector_type_, 0);
8 } else {
9 // Don't delay for debug builds since we may want to stress test the GC.
10 // If background_collector_type_ is kCollectorTypeHomogeneousSpaceCompact then we have
11 // special handling which does a homogenous space compaction once but then doesn't transition
12 // the collector.
13 RequestCollectorTransition(background_collector_type_,
14 kIsDebugBuild ? 0 : kCollectorTransitionWait);
15 }
16 }
17}
這個函數定義在文件art/runtime/gc/heap.cc中。
Heap類的成員變量prcess_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的實現如下所示:
[cpp] view plaincopy
1void Heap::RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time) {
2 Thread* self = Thread::Current();
3 {
4 MutexLock mu(self, *heap_trim_request_lock_);
5 if (desired_collector_type_ == desired_collector_type) {
6 return;
7 }
8 heap_transition_or_trim_target_time_ =
9 std::max(heap_transition_or_trim_target_time_, NanoTime() + delta_time);
10 desired_collector_type_ = desired_collector_type;
11 }
12 SignalHeapTrimDaemon(self);
13}
這個函數定義在文件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的實現如下所示:
[cpp] view plaincopy
1void Heap::SignalHeapTrimDaemon(Thread* self) {
2 JNIEnv* env = self->GetJniEnv();
3 DCHECK(WellKnownClasses::java_lang_Daemons != nullptr);
4 DCHECK(WellKnownClasses::java_lang_Daemons_requestHeapTrim != nullptr);
5 env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
6 WellKnownClasses::java_lang_Daemons_requestHeapTrim);
7 CHECK(!env->ExceptionCheck());
8}
這個函數定義在文件art/runtime/gc/heap.cc中。
Heap類的成員函數RequestCollectorTransition通過JNI接口調用了Daemons類的靜態成員函數requestHeapTrim請求執行一次GC切換操作。
Daemons類的靜態成員函數requestHeapTrim的實現如下所示:
[java] view plaincopy
1public final class Daemons {
2 ......
3
4 public static void requestHeapTrim() {
5 synchronized (HeapTrimmerDaemon.INSTANCE) {
6 HeapTrimmerDaemon.INSTANCE.notify();
7 }
8 }
9
10 ......
11}
這個函數定義在文件libcore/libart/src/main/java/java/lang/Daemons.java中。
在前面 ART運行時垃圾收集(GC)過程分析一文中提到,Java層的java.lang.Daemons類在加載的時候,會啟動五個與堆或者GC相關的守護線程,其中一個守護線程就是HeapTrimmerDaemon,這里通過調用它的成員函數notify來喚醒它。
HeapTrimmerDaemon原先被Block在成員函數run中,當它被喚醒之后 ,就會繼續執行它的成員函數run,如下所示:
[java] view plaincopy
1public final class Daemons {
2 ......
3
4 private static class HeapTrimmerDaemon extends Daemon {
5 private static final HeapTrimmerDaemon INSTANCE = new HeapTrimmerDaemon();
6
7 @Override public void run() {
8 while (isRunning()) {
9 try {
10 synchronized (this) {
11 wait();
12 }
13 VMRuntime.getRuntime().trimHeap();
14 } catch (InterruptedException ignored) {
15 }
16 }
17 }
18 }
19
20 ......
21}
這個函數定義在文件libcore/libart/src/main/java/java/lang/Daemons.java中。
從這里就可以看到,HeapTrimmerDaemon被喚醒之后,就會調用VMRuntime類的成員函數trimHeap來執行GC切換操作。
VMRuntime類的成員函數trimHeap是一個Native函數,由C++層的函數VMRuntime_trimHeap實現,如下所示:
[cpp] view plaincopy
1static void VMRuntime_trimHeap(JNIEnv*, jobject) {
2 Runtime::Current()->GetHeap()->DoPendingTransitionOrTrim();
3}
這個函數定義在文件art/runtime/native/dalvik_system_VMRuntime.cc 。
函數VMRuntime_trimHeap又是通過調用Heap類的成員函數DoPendingTransitionOrTrim來執行GC切換操作的,如下所示:
[cpp] view plaincopy
1void Heap::DoPendingTransitionOrTrim() {
2 Thread* self = Thread::Current();
3 CollectorType desired_collector_type;
4 // Wait until we reach the desired transition time.
5 while (true) {
6 uint64_t wait_time;
7 {
8 MutexLock mu(self, *heap_trim_request_lock_);
9 desired_collector_type = desired_collector_type_;
10 uint64_t current_time = NanoTime();
11 if (current_time >= heap_transition_or_trim_target_time_) {
12 break;
13 }
14 wait_time = heap_transition_or_trim_target_time_ - current_time;
15 }
16 ScopedThreadStateChange tsc(self, kSleeping);
17 usleep(wait_time / 1000); // Usleep takes microseconds.
18 }
19 // Launch homogeneous space compaction if it is desired.
20 if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
21 if (!CareAboutPauseTimes()) {
22 PerformHomogeneousSpaceCompact();
23 }
24 // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory.
25 desired_collector_type = collector_type_;
26 return;
27 }
28 // Transition the collector if the desired collector type is not the same as the current
29 // collector type.
30 TransitionCollector(desired_collector_type);
31 ......
32 // Do a heap trim if it is needed.
33 Trim();
34}
這個函數定義在文件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為新創建對象分配內存的過程分析一文。
Heap類的成員函數CareAboutPauseTimes實際上是判斷進程的當前狀態是否是用戶可感知的,即是否等于kProcessStateJankPerceptible。如果是的話,就說明它在乎GC執行時帶來的暫停時間。它的實現如下所示:
[cpp] view plaincopy
1class Heap {
2 public:
3 ......
4
5 // Returns true if we currently care about pause times.
6 bool CareAboutPauseTimes() const {
7 return process_state_ == kProcessStateJankPerceptible;
8 }
9 ......
10};
這個函數定義在文件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)過程分析一文。
接下來我們繼續分析Heap類的成員函數TransitionCollector的實現,以便了解GC的切換過程,如下所示:
[cpp] view plaincopy
1void Heap::TransitionCollector(CollectorType collector_type) {
2 if (collector_type == collector_type_) {
3 return;
4 }
5 ......
6 ThreadList* const tl = runtime->GetThreadList();
7 ......
8 // Busy wait until we can GC (StartGC can fail if we have a non-zero
9 // compacting_gc_disable_count_, this should rarely occurs).
10 for (;;) {
11 {
12 ScopedThreadStateChange tsc(self, kWaitingForGcToComplete);
13 MutexLock mu(self, *gc_complete_lock_);
14 // Ensure there is only one GC at a time.
15 WaitForGcToCompleteLocked(kGcCauseCollectorTransition, self);
16 // Currently we only need a heap transition if we switch from a moving collector to a
17 // non-moving one, or visa versa.
18 const bool copying_transition = IsMovingGc(collector_type_) != IsMovingGc(collector_type);
19 // If someone else beat us to it and changed the collector before we could, exit.
20 // This is safe to do before the suspend all since we set the collector_type_running_ before
21 // we exit the loop. If another thread attempts to do the heap transition before we exit,
22 // then it would get blocked on WaitForGcToCompleteLocked.
23 if (collector_type == collector_type_) {
24 return;
25 }
26 // GC can be disabled if someone has a used GetPrimitiveArrayCritical but not yet released.
27 if (!copying_transition || disable_moving_gc_count_ == 0) {
28 // TODO: Not hard code in semi-space collector?
29 collector_type_running_ = copying_transition ? kCollectorTypeSS : collector_type;
30 break;
31 }
32 }
33 usleep(1000);
34 }
35 tl->SuspendAll();
36 switch (collector_type) {
37 case kCollectorTypeSS: {
38 if (!IsMovingGc(collector_type_)) {
39 // Create the bump pointer space from the backup space.
40 ......
41 std::unique_ptr<MemMap> mem_map(main_space_backup_->ReleaseMemMap());
42 // We are transitioning from non moving GC -> moving GC, since we copied from the bump
43 // pointer space last transition it will be protected.
44 .....
45 mem_map->Protect(PROT_READ | PROT_WRITE);
46 bump_pointer_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space",
47 mem_map.release());
48 AddSpace(bump_pointer_space_);
49 Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition);
50 // Use the now empty main space mem map for the bump pointer temp space.
51 mem_map.reset(main_space_->ReleaseMemMap());
52 // Unset the pointers just in case.
53 if (dlmalloc_space_ == main_space_) {
54 dlmalloc_space_ = nullptr;
55 } else if (rosalloc_space_ == main_space_) {
56 rosalloc_space_ = nullptr;
57 }
58 // Remove the main space so that we don't try to trim it, this doens't work for debug
59 // builds since RosAlloc attempts to read the magic number from a protected page.
60 RemoveSpace(main_space_);
61 RemoveRememberedSet(main_space_);
62 delete main_space_; // Delete the space since it has been removed.
63 main_space_ = nullptr;
64 RemoveRememberedSet(main_space_backup_.get());
65 main_space_backup_.reset(nullptr); // Deletes the space.
66 temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
67 mem_map.release());
68 AddSpace(temp_space_);
69 }
70 break;
71 }
72 case kCollectorTypeMS:
73 // Fall through.
74 case kCollectorTypeCMS: {
75 if (IsMovingGc(collector_type_)) {
76 ......
77 std::unique_ptr<MemMap> mem_map(temp_space_->ReleaseMemMap());
78 RemoveSpace(temp_space_);
79 temp_space_ = nullptr;
80 mem_map->Protect(PROT_READ | PROT_WRITE);
81 CreateMainMallocSpace(mem_map.get(), kDefaultInitialSize, mem_map->Size(),
82 mem_map->Size());
83 mem_map.release();
84 // Compact to the main space from the bump pointer space, don't need to swap semispaces.
85 AddSpace(main_space_);
86 Compact(main_space_, bump_pointer_space_, kGcCauseCollectorTransition);
87 mem_map.reset(bump_pointer_space_->ReleaseMemMap());
88 RemoveSpace(bump_pointer_space_);
89 bump_pointer_space_ = nullptr;
90 const char* name = kUseRosAlloc ? kRosAllocSpaceName[1] : kDlMallocSpaceName[1];
91 ......
92 main_space_backup_.reset(CreateMallocSpaceFromMemMap(mem_map.get(), kDefaultInitialSize,
93 mem_map->Size(), mem_map->Size(),
94 name, true));
95 ......
96 mem_map.release();
97 }
98 break;
99 }
100 default: {
101 ......
102 break;
103 }
104 ChangeCollector(collector_type);
105 tl->ResumeAll();
106 ......
107}
這個函數定義在文件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為新創建對象分配內存的過程分析一文已經分析過了,它主要通過執行一次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堆創建過程分析一文可以知道,當原來的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堆創建過程分析一文可以知道,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堆創建過程分析一文可以知道,當原來的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為新創建對象分配內存的過程分析一文。
至此,ART運行時Foreground GC和Background GC的切換過程分析就分析完成了,ART運行時引進的Compacting GC的學習計劃也已完成,重新學習可以參考ART運行時Compacting GC簡要介紹和學習計劃一文。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。