void GarbageCollector::Run() {
ThreadList* thread_list = Runtime::Current()->GetThreadList();
uint64_t start_time = NanoTime();
pause_times_.clear();
duration_ns_ = 0;
InitializePhase();
if (!IsConcurrent()) {
// Pause is the entire length of the GC.
uint64_t pause_start = NanoTime();
ATRACE_BEGIN("Application threads suspended");
thread_list->SuspendAll();
MarkingPhase();
ReclaimPhase();
thread_list->ResumeAll();
ATRACE_END();
uint64_t pause_end = NanoTime();
pause_times_.push_back(pause_end - pause_start);
} else {
Thread* self = Thread::Current();
{
ReaderMutexLock mu(self, *Locks::mutator_lock_);
MarkingPhase();
}
bool done = false;
while (!done) {
uint64_t pause_start = NanoTime();
ATRACE_BEGIN("Suspending mutator threads");
thread_list->SuspendAll();
ATRACE_END();
ATRACE_BEGIN("All mutator threads suspended");
done = HandleDirtyObjectsPhase();
ATRACE_END();
uint64_t pause_end = NanoTime();
ATRACE_BEGIN("Resuming mutator threads");
thread_list->ResumeAll();
ATRACE_END();
pause_times_.push_back(pause_end - pause_start);
}
{
ReaderMutexLock mu(self, *Locks::mutator_lock_);
ReclaimPhase();
}
}
uint64_t end_time = NanoTime();
duration_ns_ = end_time - start_time;
FinishPhase();
}
这个函数定义在文件art/runtime/gc/collector/garbage_collector.cc中。
GarbageCollector类的成员函数Run的实现就对应于图1所示的左边和右边的两个流程。
图1所示的左边流程是用来执行非并行GC的,过程如下所示:
- 调用子类实现的成员函数InitializePhase执行GC初始化阶段。
- 挂起所有的ART运行时线程。
- 调用子类实现的成员函数MarkingPhase执行GC标记阶段。
- 调用子类实现的成员函数ReclaimPhase执行GC回收阶段。
- 恢复第2步挂起的ART运行时线程。
- 调用子类实现的成员函数FinishPhase执行GC结束阶段。
图1所示的右边流程是用来执行并行GC的,过程如下所示:
- 调用子类实现的成员函数InitializePhase执行GC初始化阶段。
- 获取用于访问Java堆的锁。
- 调用子类实现的成员函数MarkingPhase执行GC并行标记阶段。
- 释放用于访问Java堆的锁。
- 挂起所有的ART运行时线程。
- 调用子类实现的成员函数HandleDirtyObjectsPhase处理在GC并行标记阶段被修改的对象。。
- 恢复第4步挂起的ART运行时线程。
- 重复第5到第7步,直到所有在GC并行阶段被修改的对象都处理完成。
- 获取用于访问Java堆的锁。
- 调用子类实现的成员函数ReclaimPhase执行GC回收阶段。
- 释放用于访问Java堆的锁。
- 调用子类实现的成员函数FinishPhase执行GC结束阶段。
从上面的分析就可以看出,并行GC和非并行GC的区别在于:
- 非并行GC的标记阶段和回收阶段是在挂住所有的ART运行时线程的前提下进行的,因此,只需要执行一次标记即可。
- 并行GC的标记阶段只锁住了Java堆,因此它不能阻止那些不是正在分配对象的ART运行时线程同时运行,而这些同进运行的ART运行时线程可能会引用了一些在之前的标记阶段没有被标记的对象。如果不对这些对象进行重新标记的话,那么就会导致它们被GC回收,造成错误。因此,与非并行GC相比,并行GC多了一个处理脏对象的阶段。所谓的脏对象就是我们前面说的在GC标记阶段同时运行的ART运行时线程访问或者修改过的对象。
- 并行GC并不是自始至终都是并行的,例如,处理脏对象的阶段就是需要挂起除GC线程以外的其它ART运行时线程,这样才可以保证标记阶段可以结束。