ART运行时Foreground GC和Background GC切换过程分析.doc_第1页
ART运行时Foreground GC和Background GC切换过程分析.doc_第2页
ART运行时Foreground GC和Background GC切换过程分析.doc_第3页
ART运行时Foreground GC和Background GC切换过程分析.doc_第4页
ART运行时Foreground GC和Background GC切换过程分析.doc_第5页
已阅读5页,还剩13页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

ART运行时Foreground GC和Background GC切换过程分析通过前面一系列文章的学习,我们知道了ART运行时既支持Mark-Sweep GC,又支持Compacting GC。其中,Mark-Sweep GC执行效率更高,但是存在内存碎片问题;而Compacting GC执行效率较低,但是不存在内存碎片问题。ART运行时通过引入Foreground GC和Background GC的概念来对这两种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,或者从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的切换过程。 从前面这个系列的文章可以知道,应用程序组件是通过ActivityManagerService进行启动的。例如,当我们从Launcher启动一个应用程序时,实际的是在这个应用程序中Action和Category分别被配置为MAIN和LAUNCHER的Activity。这个Activity最终由ActivityManagerService通知其所在的进程进行启动工作的,也就是通过ApplicationThread类的成员函数scheduleLaunchActivity开始执行启动工作的。其它类型的组件的启动过程也是类似的,这里我们仅以Activity的启动过程作为示例,来说明ART运行时如何知道要进行Foreground GC和Background GC切换的。 ApplicationThread类的成员函数scheduleLaunchActivity的实现如下所示:java view plain copypublic 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 pendingResults, List pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) updateProcessState(procState, false); ActivityClientRecord r = new ActivityClientRecord(); r.token = token; r.ident = ident; ent = intent; r.voiceInteractor = voiceInteractor; r.activityInfo = info; patInfo = compatInfo; r.state = state; r.persistentState = persistentState; r.pendingResults = pendingResults; r.pendingIntents = pendingNewIntents; r.startsNotResumed = notResumed; r.isForward = isForward; filerInfo = 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,这个过程可以参考前面这个系列的文章。ApplicationThread类的成员函数scheduleLaunchActivity还调用了另外一个成员函数updatePendingConfiguration将参数curConfig描述的系统当前配置信息保存下来待后面处理。 我们主要关注ApplicationThread类的成员函数updateProcessState,因为它涉及到进程状态的更新,它的实现如下所示:java view plain copypublic 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 GetHeap()-UpdateProcessState(static_cast(process_state); . 这个函数定义在文件art/runtime/native/dalvik_system_VMRuntime.cc中。 函数VMRuntime_updateProcessState主要是调用了Heap类的成员函数UpdateProcessState来通知ART运行时切换Foreground GC和Background GC,后者的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片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 / Dont 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 doesnt 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的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片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的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片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的实现如下所示:java view plain copy 在CODE上查看代码片派生到我的代码片public final class Daemons . public static void requestHeapTrim() synchronized (HeapTrimmerDaemon.INSTANCE) HeapTrimmerDaemon.INSTANCE.notify(); . 这个函数定义在文件libcore/libart/src/main/Java/java/lang/Daemons.java中。 在前面一文中提到,Java层的java.lang.Daemons类在加载的时候,会启动五个与堆或者GC相关的守护线程,其中一个守护线程就是HeapTrimmerDaemon,这里通过调用它的成员函数notify来唤醒它。 HeapTrimmerDaemon原先被Blocked在成员函数run中,当它被唤醒之后 ,就会继续执行它的成员函数run,如下所示:java view plain copy 在CODE上查看代码片派生到我的代码片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实现,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片static void VMRuntime_trimHeap(JNIEnv*, jobject) Runtime:Current()-GetHeap()-DoPendingTransitionOrTrim(); 这个函数定义在文件art/runtime/native/dalvik_system_VMRuntime.cc 。 函数VMRuntime_trimHeap又是通过调用Heap类的成员函数DoPendingTransitionOrTrim来执行GC切换操作的,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片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执行同构空间压缩的过程,可以参考前面一文。 Heap类的成员函数CareAboutPauseTimes实际上是判断进程的当前状态是否是用户可感知的,即是否等于kProcessStateJankPerceptible。如果是的话,就说明它在乎GC执行时带来的暂停时间。它的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片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

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论