ART运行时垃圾收集(GC)过程分析

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

  ART运行时与Dalvik虚拟机一样,都使用了Mark-Sweep算法进行垃圾回收,因此它们的垃圾回收流程在总体上是一致的。但是ART运行时对堆的划分更加细致,因而在此基础上实现了更多样的回收策略。不同的策略有不同的回收力度,力度越大的回收策略,每次回收的内存就越多,并且它们都有各自的使用情景。这样就可以使得每次执行GC时,可以最大限度地减少应用程序停顿。本文就详细分析ART运行时的垃圾收集过程。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

        ART运行时的垃圾收集收集过程如图1所示:

图1 ART运行时的GC执行流程

        图1的最上面三个箭头描述触发GC的三种情况,左边的流程图描述非并行GC的执行过程,右边的流程图描述并行GC的执行流程,接下来我们就详细图中涉及到的所有细节。

        在前面ART运行时为新创建对象分配内存的过程分析一文中,我们提到了两种可能会触发GC的情况。第一种情况是没有足够内存分配请求的分存时,会调用Heap类的成员函数CollectGarbageInternal触发一个原因为kGcCauseForAlloc的GC。第二种情况下分配出请求的内存之后,堆剩下的内存超过一定的阀值,就会调用Heap类的成员函数RequestConcurrentGC请求执行一个并行GC。

        Heap类的成员函数RequestConcurrentGC的实现如下所示:

[cpp]  view plain copy
  1. void Heap::RequestConcurrentGC(Thread* self) {  
  2.   // Make sure that we can do a concurrent GC.  
  3.   Runtime* runtime = Runtime::Current();  
  4.   DCHECK(concurrent_gc_);  
  5.   if (runtime == NULL || !runtime->IsFinishedStarting() ||  
  6.       !runtime->IsConcurrentGcEnabled()) {  
  7.     return;  
  8.   }  
  9.   {  
  10.     MutexLock mu(self, *Locks::runtime_shutdown_lock_);  
  11.     if (runtime->IsShuttingDown()) {  
  12.       return;  
  13.     }  
  14.   }  
  15.   if (self->IsHandlingStackOverflow()) {  
  16.     return;  
  17.   }  
  18.   
  19.   // We already have a request pending, no reason to start more until we update  
  20.   // concurrent_start_bytes_.  
  21.   concurrent_start_bytes_ = std::numeric_limits<size_t>::max();  
  22.   
  23.   JNIEnv* env = self->GetJniEnv();  
  24.   DCHECK(WellKnownClasses::java_lang_Daemons != NULL);  
  25.   DCHECK(WellKnownClasses::java_lang_Daemons_requestGC != NULL);  
  26.   env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,  
  27.                             WellKnownClasses::java_lang_Daemons_requestGC);  
  28.   CHECK(!env->ExceptionCheck());  
  29. }  
       这个函数定义在文件art/runtime/gc/heap.cc。

       只有满足以下四个条件,Heap类的成员函数RequestConcurrentGC才会触发一个并行GC:

       1. ART运行时已经启动完毕。

       2. ART运行时支持并行GC。ART运行时默认是支持并行GC的,但是可以通过启动选项-Xgc来关闭。

       3. ART运行时不是正在关闭。

       4. 当前线程没有发生栈溢出。

       上述4个条件都满足之后,Heap类的成员函数RequestConcurrentGC就将成员变量concurrent_start_bytes_的值设置为类型size_t的最大值,表示目前正有一个并行GC在等待执行,以阻止触发另外一个并行GC。

       最后,Heap类的成员函数RequestConcurrentGC调用Java层的java.lang.Daemons类的静态成员函数requestGC请求执行一次并行GC。Java层的java.lang.Daemons类在加载的时候,会启动五个与堆或者GC相关的守护线程,如下所示:

[java]  view plain copy
  1. public final class Daemons {  
  2.     ......  
  3.   
  4.     public static void start() {  
  5.         ReferenceQueueDaemon.INSTANCE.start();  
  6.         FinalizerDaemon.INSTANCE.start();  
  7.         FinalizerWatchdogDaemon.INSTANCE.start();  
  8.         HeapTrimmerDaemon.INSTANCE.start();  
  9.         GCDaemon.INSTANCE.start();  
  10.     }  
  11.   
  12.     ......  
  13. }  
       这个类定义在文件libcore/libart/src/main/java/java/lang/Daemons.java中。

       这五个守护线程分别是:

       1. ReferenceQueueDaemon:引用队列守护线程。我们知道,在创建引用对象的时候,可以关联一个队列。当被引用对象引用的对象被GC回收的时候,被引用对象就会被加入到其创建时关联的队列去。这个加入队列的操作就是由ReferenceQueueDaemon守护线程来完成的。这样应用程序就可以知道那些被引用对象引用的对象已经被回收了。

       2. FinalizerDaemon:析构守护线程。对于重写了成员函数finalize的对象,它们被GC决定回收时,并没有马上被回收,而是被放入到一个队列中,等待FinalizerDaemon守护线程去调用它们的成员函数finalize,然后再被回收。

       3. FinalizerWatchdogDaemon:析构监护守护线程。用来监控FinalizerDaemon线程的执行。一旦检测那些重定了成员函数finalize的对象在执行成员函数finalize时超出一定的时候,那么就会退出VM。

       4. HeapTrimmerDaemon:堆裁剪守护线程。用来执行裁剪堆的操作,也就是用来将那些空闲的堆内存归还给系统。

       5. GCDaemon:并行GC线程。用来执行并行GC。

       Java层的java.lang.Daemons类的静态成员函数requestGC被调用时,就会唤醒上述的并行GC线程,然后这个并行GC线程就会通过JNI调用Heap类的成员函数ConcurrentGC,它的实现如下所示:

[cpp]  view plain copy
  1. void Heap::ConcurrentGC(Thread* self) {  
  2.   {  
  3.     MutexLock mu(self, *Locks::runtime_shutdown_lock_);  
  4.     if (Runtime::Current()->IsShuttingDown()) {  
  5.       return;  
  6.     }  
  7.   }  
  8.   
  9.   // Wait for any GCs currently running to finish.  
  10.   if (WaitForConcurrentGcToComplete(self) == collector::kGcTypeNone) {  
  11.     CollectGarbageInternal(next_gc_type_, kGcCauseBackground, false);  
  12.   }  
  13. }  
        这个函数定义在文件art/runtime/gc/heap.cc中。

        只要ART运行时当前不是处于正在关闭的状态,那么Heap类的成员函数ConcurrentGC就会检查当前是否正在执行GC。如果是的话,那么就等待它执行完成,然后再调用Heap类的成员函数CollectGarbageInternal触发一个原因为kGcCauseBackground的GC。否则的话,就直接调用Heap类的成员函数CollectGarbageInternal触发一个原因为kGcCauseBackground的GC。

        从这里就可以看到,无论是触发GC的原因是kGcCauseForAlloc,还是kGcCauseBackground,最终都是通过调用Heap类的成员函数CollectGarbageInternal来执行GC的。此外,还有第三种情况会触发GC,如下所示:

[cpp]  view plain copy
  1. void Heap::CollectGarbage(bool clear_soft_references) {  
  2.   // Even if we waited for a GC we still need to do another GC since weaks allocated during the  
  3.   // last GC will not have necessarily been cleared.  
  4.   Thread* self = Thread::Current();  
  5.   WaitForConcurrentGcToComplete(self);  
  6.   CollectGarbageInternal(collector::kGcTypeFull, kGcCauseExplicit, clear_soft_references);  
  7. }  
        这个函数定义在文件art/runtime/gc/heap.cc。

        当我们调用Java层的java.lang.System的静态成员函数gc时,如果ART运行时支持显式GC,那么就它就会通过JNI调用Heap类的成员函数CollectGarbageInternal来触发一个原因为kGcCauseExplicit的GC。ART运行时默认是支持显式GC的,但是可以通过启动选项-XX:+DisableExplicitGC来关闭。

        从上面的分析就可以看出,ART运行时在三种情况下会触发GC,这三种情况通过三个枚举kGcCauseForAlloc、kGcCauseBackground和kGcCauseExplicitk来描述。这三人枚举的定义如下所示:

[cpp]  view plain copy
  1. // What caused the GC?  
  2. enum GcCause {  
  3.   // GC triggered by a failed allocation. Thread doing allocation is blocked waiting for GC before  
  4.   // retrying allocation.  
  5.   kGcCauseForAlloc,  
  6.   // A background GC trying to ensure there is free memory ahead of allocations.  
  7.   kGcCauseBackground,  
  8.   // An explicit System.gc() call.  
  9.   kGcCauseExplicit,  
  10. };  
       这三个枚举定义在文件art/runtime/gc/heap.h中。

       从上面的分析还可以看出,ART运行时的所有GC都是以Heap类的成员函数CollectGarbageInternal为入口,它的实现如下所示:

[cpp]  view plain copy
  1. collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCause gc_cause,  
  2.                                                bool clear_soft_references) {  
  3.   Thread* self = Thread::Current();  
  4.   ......  
  5.   
  6.   // Ensure there is only one GC at a time.  
  7.   bool start_collect = false;  
  8.   while (!start_collect) {  
  9.     {  
  10.       MutexLock mu(self, *gc_complete_lock_);  
  11.       if (!is_gc_running_) {  
  12.         is_gc_running_ = true;  
  13.         start_collect = true;  
  14.       }  
  15.     }  
  16.     if (!start_collect) {  
  17.       // TODO: timinglog this.  
  18.       WaitForConcurrentGcToComplete(self);  
  19.       ......  
  20.     }  
  21.   }  
  22.   
  23.   ......  
  24.   
  25.   if (gc_type == collector::kGcTypeSticky &&  
  26.       alloc_space_->Size() < min_alloc_space_size_for_sticky_gc_) {  
  27.     gc_type = collector::kGcTypePartial;  
  28.   }  
  29.   
  30.   ......  
  31.   
  32.   collector::MarkSweep* collector = NULL;  
  33.   for (const auto& cur_collector : mark_sweep_collectors_) {  
  34.     if (cur_collector->IsConcurrent() == concurrent_gc_ && cur_collector->GetGcType() == gc_type) {  
  35.       collector = cur_collector;  
  36.       break;  
  37.     }  
  38.   }  
  39.   ......  
  40.   
  41.   collector->clear_soft_references_ = clear_soft_references;  
  42.   collector->Run();  
  43.   ......  
  44.   
  45.   {  
  46.       MutexLock mu(self, *gc_complete_lock_);  
  47.       is_gc_running_ = false;  
  48.       last_gc_type_ = gc_type;  
  49.       // Wake anyone who may have been waiting for the GC to complete.  
  50.       gc_complete_cond_->Broadcast(self);  
  51.   }  
  52.   
  53.   ......  
  54.   
  55.   return gc_type;  
  56. }  
        这个函数定义在文件art/runtime/gc/heap.cc。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block var foo = 'bar'; 

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目 Value
电脑 $1600
手机 $12
导管 $1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列 第二列 第三列
第一列文本居中 第二列文本居右 第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPE ASCII HTML
Single backticks 'Isn't this fun?' ‘Isn’t this fun?’
Quotes "Isn't this fun?" “Isn’t this fun?”
Dashes -- is en-dash, --- is em-dash – is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

gantt
        dateFormat  YYYY-MM-DD
        title Adding GANTT diagram functionality to mermaid
        section 现有任务
        已完成               :done,    des1, 2014-01-06,2014-01-08
        进行中               :active,  des2, 2014-01-09, 3d
        计划一               :         des3, after des2, 5d
        计划二               :         des4, after des3, 5d
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

你可能感兴趣的:(ART运行时垃圾收集(GC)过程分析)