art运行时垃圾收集gc过程分析doc.docx
- 文档编号:6621303
- 上传时间:2023-01-08
- 格式:DOCX
- 页数:90
- 大小:134.77KB
art运行时垃圾收集gc过程分析doc.docx
《art运行时垃圾收集gc过程分析doc.docx》由会员分享,可在线阅读,更多相关《art运行时垃圾收集gc过程分析doc.docx(90页珍藏版)》请在冰豆网上搜索。
art运行时垃圾收集gc过程分析doc
ART运行时垃圾收集(GC)过程分析
ART运行时与Dalvik虚拟机一样,都使用了Mark-Sweep算法进行垃圾回收,因此它们的垃圾回收流程在总体上是一致的。
但是ART运行时对堆的划分更加细致,因而在此基础上实现了更多样的回收策略。
不同的策略有不同的回收力度,力度越大的回收策略,每次回收的内存就越多,并且它们都有各自的使用情景。
这样就可以使得每次执行GC时,可以最大限度地减少应用程序停顿。
本文就详细分析ART运行时的垃圾收集过程。
ART运行时的垃圾收集收集过程如图1所示:
图1的最上面三个箭头描述触发GC的三种情况,左边的流程图描述非并行GC的执行过程,右边的流程图描述并行GC的执行流程,接下来我们就详细图中涉及到的所有细节。
在前面一文中,我们提到了两种可能会触发GC的情况。
第一种情况是没有足够内存分配请求的分存时,会调用Heap类的成员函数CollectGarbageInternal触发一个原因为kGcCauseForAlloc的GC。
第二种情况下分配出请求的内存之后,堆剩下的内存超过一定的阀值,就会调用Heap类的成员函数RequestConcurrentGC请求执行一个并行GC。
Heap类的成员函数RequestConcurrentGC的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
voidHeap:
:
RequestConcurrentGC(Thread*self){
//MakesurethatwecandoaconcurrentGC.
Runtime*runtime=Runtime:
:
Current();
DCHECK(concurrent_gc_);
if(runtime==NULL||!
runtime->IsFinishedStarting()||
!
runtime->IsConcurrentGcEnabled()){
return;
}
{
MutexLockmu(self,*Locks:
:
runtime_shutdown_lock_);
if(runtime->IsShuttingDown()){
return;
}
}
if(self->IsHandlingStackOverflow()){
return;
}
//Wealreadyhavearequestpending,noreasontostartmoreuntilweupdate
//concurrent_start_bytes_.
concurrent_start_bytes_=std:
:
numeric_limits
:
max();
JNIEnv*env=self->GetJniEnv();
DCHECK(WellKnownClasses:
:
java_lang_Daemons!
=NULL);
DCHECK(WellKnownClasses:
:
java_lang_Daemons_requestGC!
=NULL);
env->CallStaticVoidMethod(WellKnownClasses:
:
java_lang_Daemons,
WellKnownClasses:
:
java_lang_Daemons_requestGC);
CHECK(!
env->ExceptionCheck());
}
这个函数定义在文件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]viewplaincopy在CODE上查看代码片派生到我的代码片
publicfinalclassDaemons{
......
publicstaticvoidstart(){
ReferenceQueueDaemon.INSTANCE.start();
FinalizerDaemon.INSTANCE.start();
FinalizerWatchdogDaemon.INSTANCE.start();
HeapTrimmerDaemon.INSTANCE.start();
GCDaemon.INSTANCE.start();
}
......
}
这个类定义在文件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]viewplaincopy在CODE上查看代码片派生到我的代码片
voidHeap:
:
ConcurrentGC(Thread*self){
{
MutexLockmu(self,*Locks:
:
runtime_shutdown_lock_);
if(Runtime:
:
Current()->IsShuttingDown()){
return;
}
}
//WaitforanyGCscurrentlyrunningtofinish.
if(WaitForConcurrentGcToComplete(self)==collector:
:
kGcTypeNone){
CollectGarbageInternal(next_gc_type_,kGcCauseBackground,false);
}
}
这个函数定义在文件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]viewplaincopy在CODE上查看代码片派生到我的代码片
voidHeap:
:
CollectGarbage(boolclear_soft_references){
//EvenifwewaitedforaGCwestillneedtodoanotherGCsinceweaksallocatedduringthe
//lastGCwillnothavenecessarilybeencleared.
Thread*self=Thread:
:
Current();
WaitForConcurrentGcToComplete(self);
CollectGarbageInternal(collector:
:
kGcTypeFull,kGcCauseExplicit,clear_soft_references);
}
这个函数定义在文件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]viewplaincopy在CODE上查看代码片派生到我的代码片
//WhatcausedtheGC?
enumGcCause{
//GCtriggeredbyafailedallocation.ThreaddoingallocationisblockedwaitingforGCbefore
//retryingallocation.
kGcCauseForAlloc,
//AbackgroundGCtryingtoensurethereisfreememoryaheadofallocations.
kGcCauseBackground,
//AnexplicitSystem.gc()call.
kGcCauseExplicit,
};
这三个枚举定义在文件art/runtime/gc/heap.h中。
从上面的分析还可以看出,ART运行时的所有GC都是以Heap类的成员函数CollectGarbageInternal为入口,它的实现如下所示:
[cpp]viewplaincopy在CODE上查看代码片派生到我的代码片
collector:
:
GcTypeHeap:
:
CollectGarbageInternal(collector:
:
GcTypegc_type,GcCausegc_cause,
boolclear_soft_references){
Thread*self=Thread:
:
Current();
......
//EnsurethereisonlyoneGCatatime.
boolstart_collect=false;
while(!
start_collect){
{
MutexLockmu(self,*gc_complete_lock_);
if(!
is_gc_running_){
is_gc_running_=true;
start_collect=true;
}
}
if(!
start_collect){
//TODO:
timinglogthis.
WaitForConcurrentGcToComplete(self);
......
}
}
......
if(gc_type==collector:
:
kGcTypeSticky&&
alloc_space_->Size() gc_type=collector: : kGcTypePartial; } ...... collector: : MarkSweep*collector=NULL; for(constauto&cur_collector: mark_sweep_collectors_){ if(cur_collector->IsConcurrent()==concurrent_gc_&&cur_collector->GetGcType()==gc_type){ collector=cur_collector; break; } } ...... collector->clear_soft_references_=clear_soft_references; collector->Run(); ...... { MutexLockmu(self,*gc_complete_lock_); is_gc_running_=false; last_gc_type_=gc_type; //WakeanyonewhomayhavebeenwaitingfortheGCtocomplete. gc_complete_cond_->Broadcast(self); } ...... returngc_type; } 这个函数定义在文件art/runtime/gc/heap.cc。 参数gc_type和gc_cause分别用来描述要执行的GC的类型和原因,而参数clear_soft_references用来描述是否要回收被软引用对象引用的对象。 Heap类的成员函数CollectGarbageInternal的执行逻辑如下所示: 1.通过一个while循环不断地检查Heap类的成员变量is_gc_running_,直到它的值等于false为止,这表示当前没有其它线程正在执行GC。 当它的值等于true时,就表示在其它线程正在执行GC,这时候就要调用Heap类的成员函数WaitForConcurrentGcToComplete等待其执行完成。 注意,在当前GC执行之前,Heap类的成员变量is_gc_running_会被设置为true。 2.如果当前请求执行的GC的类型为kGcTypeSticky,但是当前AllocationSpace的大小小于Heap类的成员变量min_alloc_space_size_for_sticky_gc_指定的阀值,那么就改为执行类型为kGcTypePartial。 关于类型为kGcTypeSticky的GC的执行限制,可以参数前面一文。 3.从Heap类的成员变量mark_sweep_collectors_指向的一个垃圾收集器列表找到一个合适的垃圾收集器来执行GC。 从前面一文可以知道,ART运行时在内部创建了六个垃圾收集器。 这六个垃圾收集器分为两组,一组支持并行GC,另一组不支持。 每一组都是由三个类型分别为kGcTypeSticky、kGcTypePartial和kGcTypeFull的垃垃圾收集器组成。 这里说的合适的垃圾收集器,是指并行性与Heap类的成员变量concurrent_gc_一致,并且类型也与参数gc_type一致的垃圾收集器。 4.找到合适的垃圾收集器之后,就将参数clear_soft_references的值保存它的成员变量clear_soft_references_中,以便可以告诉它要不要回收被软引用对象引用的对象,然后再调用它的成员函数Run来执行GC。 5.GC执行完毕,将Heap类的成员变量is_gc_running_设置为false,以表示当前GC已经执行完毕,下一次请求的GC可以执行了。 此外,也会将Heap类的成员变量last_gc_type_设置为当前执行的GC的类型。 这样下一次执行GC时,就可以执行另外一个不同类型的GC。 例如,如果上一次执行的GC的类型为kGcTypeSticky,那么接下来的两次GC的类型就可以设置为kGcTypePartial和kGcTypeFull,这样可以使得每次都能执行有效的GC。 6.通过Heap类的成员变量gc_complete_cond_唤醒那些正在等待GC执行完成的线程。 在上面的六个步骤中,最重要的就是第四步了。 从前面一文可以知道,所有的垃圾收集器都是从GarbageCollector类继承下来的,因此上面的第四步实际上执行的是GarbageCollector类的成员函数Run,它的实现如下所示: [cpp]viewplaincopy在CODE上查看代码片派生到我的代码片 voidGarbageCollector: : Run(){ ThreadList*thread_list=Runtime: : Current()->GetThreadList(); uint64_tstart_time=NanoTime(); pause_times_.clear(); duration_ns_=0; InitializePhase(); if(! IsConcurrent()){ //PauseistheentirelengthoftheGC. uint64_tpause_start=NanoTime(); ATRACE_BEGIN("Applicationthreadssuspended"); thread_list->SuspendAll(); MarkingPhase(); ReclaimPhase(); thread_list->ResumeAll(); ATRACE_END(); uint64_tpause_end=NanoTime(); pause_times_.push_back(pause_end-pause_start); }else{ Thread*self=Thread: : Current(); { ReaderMutexLockmu(self,*Locks: : mutator_lock_); MarkingPhase(); } booldone=false; while(! done){ uint64_tpause_start=NanoTime(); ATRACE_BEGIN("Suspendingmutatorthreads"); thread_list->SuspendAll(); ATRACE_END(); ATRACE_BEGIN("Allmutatorthreadssuspended"); done=HandleDirtyObjectsPhase(); ATRACE_END(); uint64_tpause_end=NanoTime(); ATRACE_BEGIN("Resumingmutatorthreads"); thread_list->ResumeAll(); ATRACE_END(); pause_times_.push_back(pause_end-pause_start); } { ReaderMutexLockmu(self,*Locks: : mutator_lock_); ReclaimPhase(); } } uint64_tend_time=NanoTime(); duration_ns_=end_time-start_time; FinishPhase(); } 这个函数定义在文件art/runtime/gc/collector/garbage_collector.cc中。 GarbageCollector类的成员函数Run的实现就对应于图1所示的左边和右边的两个流程。 图1所示的左边流程是用来执行非并行GC的,过程如下所示: 1.调用子类实现的成员函数InitializePhase执行GC初始化阶段。 2.挂起所有的ART运行时线程。 3.调用子类实现的成员函数MarkingPhase执行GC标记阶段。 4.调用子类实现的成员函数ReclaimPhase执行GC回收阶段。 5.恢复第2步挂起的ART运行时线程。 6.调用子类实现的成员函数FinishPhase执行GC结束阶段。 图1所示的右边流程是用来执行并行GC的,过程如下所示: 1.调用子类实现的成员函数InitializePhase执行GC初始化阶段。 2.获取用于访问Java堆的锁。 3.调用子类实现的成员函数MarkingPhase执行GC并行标记阶段。 4.释放用于访问Java堆的锁。 5.挂起所有的ART运行时线程。 6.调用子类实现的成员函数HandleDirtyObjectsPhase处理在GC并行标记阶段被修改的对象。 。 7.恢复第4步挂起的ART运行时线程。 8.重复第5到第7步,直到所有在GC并行阶段被修改的对象都处理完成。 9.获取用于访问Java堆的锁。 10.调用子类实现的成员函数ReclaimPhase执行GC回收阶段。 11.释放用于访问Java堆的锁。 12.调用子类实现的成员函数FinishPhase执行GC结
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- art 运行 垃圾 收集 gc 过程 分析 doc