[原文出處-------------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的時序關系,如下所示:

圖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簡要介紹和學習計劃一文
- 前言
- Android組件設計思想
- Android源代碼開發和調試環境搭建
- Android源代碼下載和編譯
- Android源代碼情景分析法
- Android源代碼調試分析法
- 手把手教你為手機編譯ROM
- 在Ubuntu上下載、編譯和安裝Android最新源代碼
- 在Ubuntu上下載、編譯和安裝Android最新內核源代碼(Linux Kernel)
- 如何單獨編譯Android源代碼中的模塊
- 在Ubuntu上為Android系統編寫Linux內核驅動程序
- 在Ubuntu上為Android系統內置C可執行程序測試Linux內核驅動程序
- 在Ubuntu上為Android增加硬件抽象層(HAL)模塊訪問Linux內核驅動程序
- 在Ubuntu為Android硬件抽象層(HAL)模塊編寫JNI方法提供Java訪問硬件服務接口
- 在Ubuntu上為Android系統的Application Frameworks層增加硬件訪問服務
- 在Ubuntu上為Android系統內置Java應用程序測試Application Frameworks層的硬件服務
- Android源代碼倉庫及其管理工具Repo分析
- Android編譯系統簡要介紹和學習計劃
- Android編譯系統環境初始化過程分析
- Android源代碼編譯命令m/mm/mmm/make分析
- Android系統鏡像文件的打包過程分析
- 從CM刷機過程和原理分析Android系統結構
- Android系統架構概述
- Android系統整體架構
- android專用驅動
- Android硬件抽象層HAL
- Android應用程序組件
- Android應用程序框架
- Android用戶界面架構
- Android虛擬機之Dalvik虛擬機
- Android硬件抽象層
- Android硬件抽象層(HAL)概要介紹和學習計劃
- Android專用驅動
- Android Logger驅動系統
- Android日志系統驅動程序Logger源代碼分析
- Android應用程序框架層和系統運行庫層日志系統源代碼分析
- Android日志系統Logcat源代碼簡要分析
- Android Binder驅動系統
- Android進程間通信(IPC)機制Binder簡要介紹和學習計劃
- 淺談Service Manager成為Android進程間通信(IPC)機制Binder守護進程之路
- 淺談Android系統進程間通信(IPC)機制Binder中的Server和Client獲得Service Manager接口之路
- Android系統進程間通信(IPC)機制Binder中的Server啟動過程源代碼分析
- Android系統進程間通信(IPC)機制Binder中的Client獲得Server遠程接口過程源代碼分析
- Android系統進程間通信Binder機制在應用程序框架層的Java接口源代碼分析
- Android Ashmem驅動系統
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)簡要介紹和學習計劃
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)驅動程序源代碼分析
- Android系統匿名共享內存Ashmem(Anonymous Shared Memory)在進程間共享的原理分析
- Android系統匿名共享內存(Anonymous Shared Memory)C++調用接口分析
- Android應用程序進程管理
- Android應用程序進程啟動過程的源代碼分析
- Android系統進程Zygote啟動過程的源代碼分析
- Android系統默認Home應用程序(Launcher)的啟動過程源代碼分析
- Android應用程序消息機制
- Android應用程序消息處理機制(Looper、Handler)分析
- Android應用程序線程消息循環模型分析
- Android應用程序輸入事件分發和處理機制
- Android應用程序鍵盤(Keyboard)消息處理機制分析
- Android應用程序UI架構
- Android系統的開機畫面顯示過程分析
- Android幀緩沖區(Frame Buffer)硬件抽象層(HAL)模塊Gralloc的實現原理分析
- SurfaceFlinger
- Android系統Surface機制的SurfaceFlinger服務
- SurfaceFlinger服務簡要介紹和學習計劃
- 啟動過程分析
- 對幀緩沖區(Frame Buffer)的管理分析
- 線程模型分析
- 渲染應用程序UI的過程分析
- Android應用程序與SurfaceFlinger服務的關系
- 概述和學習計劃
- 連接過程分析
- 共享UI元數據(SharedClient)的創建過程分析
- 創建Surface的過程分析
- 渲染Surface的過程分析
- Android應用程序窗口(Activity)
- 實現框架簡要介紹和學習計劃
- 運行上下文環境(Context)的創建過程分析
- 窗口對象(Window)的創建過程分析
- 視圖對象(View)的創建過程分析
- 與WindowManagerService服務的連接過程分析
- 繪圖表面(Surface)的創建過程分析
- 測量(Measure)、布局(Layout)和繪制(Draw)過程分析
- WindowManagerService
- WindowManagerService的簡要介紹和學習計劃
- 計算Activity窗口大小的過程分析
- 對窗口的組織方式分析
- 對輸入法窗口(Input Method Window)的管理分析
- 對壁紙窗口(Wallpaper Window)的管理分析
- 計算窗口Z軸位置的過程分析
- 顯示Activity組件的啟動窗口(Starting Window)的過程分析
- 切換Activity窗口(App Transition)的過程分析
- 顯示窗口動畫的原理分析
- Android控件TextView的實現原理分析
- Android視圖SurfaceView的實現原理分析
- Android應用程序UI硬件加速渲染
- 簡要介紹和學習計劃
- 環境初始化過程分析
- 預加載資源地圖集服務(Asset Atlas Service)分析
- Display List構建過程分析
- Display List渲染過程分析
- 動畫執行過程分析
- Android應用程序資源管理框架
- Android資源管理框架(Asset Manager)
- Asset Manager 簡要介紹和學習計劃
- 編譯和打包過程分析
- Asset Manager的創建過程分析
- 查找過程分析
- Dalvik虛擬機和ART虛擬機
- Dalvik虛擬機
- Dalvik虛擬機簡要介紹和學習計劃
- Dalvik虛擬機的啟動過程分析
- Dalvik虛擬機的運行過程分析
- Dalvik虛擬機JNI方法的注冊過程分析
- Dalvik虛擬機進程和線程的創建過程分析
- Dalvik虛擬機垃圾收集機制簡要介紹和學習計劃
- Dalvik虛擬機Java堆創建過程分析
- Dalvik虛擬機為新創建對象分配內存的過程分析
- Dalvik虛擬機垃圾收集(GC)過程分析
- ART虛擬機
- Android ART運行時無縫替換Dalvik虛擬機的過程分析
- Android運行時ART簡要介紹和學習計劃
- Android運行時ART加載OAT文件的過程分析
- Android運行時ART加載類和方法的過程分析
- Android運行時ART執行類方法的過程分析
- ART運行時垃圾收集機制簡要介紹和學習計劃
- ART運行時Java堆創建過程分析
- ART運行時為新創建對象分配內存的過程分析
- ART運行時垃圾收集(GC)過程分析
- ART運行時Compacting GC簡要介紹和學習計劃
- ART運行時Compacting GC堆創建過程分析
- ART運行時Compacting GC為新創建對象分配內存的過程分析
- ART運行時Semi-Space(SS)和Generational Semi-Space(GSS)GC執行過程分析
- ART運行時Mark-Compact( MC)GC執行過程分析
- ART運行時Foreground GC和Background GC切換過程分析
- Android安全機制
- SEAndroid安全機制簡要介紹和學習計劃
- SEAndroid安全機制框架分析
- SEAndroid安全機制中的文件安全上下文關聯分析
- SEAndroid安全機制中的進程安全上下文關聯分析
- SEAndroid安全機制對Android屬性訪問的保護分析
- SEAndroid安全機制對Binder IPC的保護分析
- 從NDK在非Root手機上的調試原理探討Android的安全機制
- APK防反編譯
- Android視頻硬解穩定性問題探討和處理
- Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析
- Android應用程序安裝過程源代碼分析
- Android應用程序啟動過程源代碼分析
- 四大組件源代碼分析
- Activity
- Android應用程序的Activity啟動過程簡要介紹和學習計劃
- Android應用程序內部啟動Activity過程(startActivity)的源代碼分析
- 解開Android應用程序組件Activity的"singleTask"之謎
- Android應用程序在新的進程中啟動新的Activity的方法和過程分析
- Service
- Android應用程序綁定服務(bindService)的過程源代碼分析
- ContentProvider
- Android應用程序組件Content Provider簡要介紹和學習計劃
- Android應用程序組件Content Provider應用實例
- Android應用程序組件Content Provider的啟動過程源代碼分析
- Android應用程序組件Content Provider在應用程序之間共享數據的原理分析
- Android應用程序組件Content Provider的共享數據更新通知機制分析
- BroadcastReceiver
- Android系統中的廣播(Broadcast)機制簡要介紹和學習計劃
- Android應用程序注冊廣播接收器(registerReceiver)的過程分析
- Android應用程序發送廣播(sendBroadcast)的過程分析