当前位置:首页 > ART运行时垃圾收集(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结束阶段。
从上面的分析就可以看出,并行GC和非并行GC的区别在于:
1. 非并行GC的标记阶段和回收阶段是在挂住所有的ART运行时线程的前提下进行的,因此,只需要执行一次标记即可。
2. 并行GC的标记阶段只锁住了Java堆,因此它不能阻止那些不是正在分配对象的ART运行时线程同时运行,而这些同进运行的ART运行时线程可能会引用了一些在之前的标记阶段没有被标记的对象。如果不对这些对象进行重新标记的话,那么就会导致它们被GC回收,造成错误。因此,与非并行GC相比,并行GC多了一个处理脏对象的阶段。所谓的脏对象就是我们前面说的在GC标记阶段同时运行的ART运行时线程访问或者修改过的对象。
3. 并行GC并不是自始至终都是并行的,例如,处理脏对象的阶段就是需要挂起除GC线程以外的其它ART运行时线程,这样才可以保证标记阶段可以结束。
从前面一文可以知道,GarbageCollector类有三个直接或者间接的子类MarkSweep、PartialMarkSweep和StickyMarkSweep都可以用来执行垃圾回收,其中,PartialMarkSweep类又是从MarkSweep类直接继承下来的,而StickyMarkSweep类是从PartialMarkSweep类直接继承下来的。MarkSweep类用来回收Zygote Space和Allocation Space的垃圾,PartialMarkSweep类用来回收Allocation Space的垃圾,StickyMarkSweep类用来回收上次GC以来在Allcation Space上分配的最终又没有被引用的垃圾。
接下来,我们就主要分析ART运行时线程的挂起和恢复过程,以及MarkSweep、PartialMarkSweep和StickyMarkSweep这三个类是执行InitializePhase、MarkingPhase、HandleDirtyObjectsPhase、ReclaimPhase和FinishPhase的五个GC阶段的过程。
1. ART运行时线程的挂起
从上面的分析可以知道,ART运行时线程的挂起是通过调用ThreadList类的成员函数SuspendAll实现的,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 void ThreadList::SuspendAll() { Thread* self = Thread::Current(); ...... {
MutexLock mu(self, *Locks::thread_list_lock_); {
MutexLock mu2(self, *Locks::thread_suspend_count_lock_); // Update global suspend all state for attaching threads. ++suspend_all_count_;
// Increment everybody's suspend count (except our own). for (const auto& thread : list_) { if (thread == self) { continue; } ......
thread->ModifySuspendCount(self, +1, false);
} } }
// Block on the mutator lock until all Runnable threads release their share of access. #if HAVE_TIMED_RWLOCK
// Timeout if we wait more than 30 seconds.
if (UNLIKELY(!Locks::mutator_lock_->ExclusiveLockWithTimeout(self, 30 * 1000, 0))) { UnsafeLogFatalForThreadSuspendAllTimeout(self); } #else
Locks::mutator_lock_->ExclusiveLock(self); #endif
...... }
这个函数定义在文件art/runtime/thread_list.cc中。
所有的ART运行时线程都保存在ThreadList类的成员变量list_描述的一个列表,遍历这个列表时,需要获取Lock类的成员变量thread_list_lock_描述的一个互斥锁。
ThreadList类有一个成员变量suspend_all_count_,用来描述全局的线程挂起计数器。在所有的ART运行时线程挂起期间,如果有新的线程将自己注册为ART运行时线程,那么它也会将自己挂起来,而判断所有的ART运行时线程是不是处于挂起期间,就是通过ThreadList类的成员变量suspend_all_count_的值是否大于0进行的。因此,ThreadList类的成员函数SuspendAll在挂起所有的ART运行时线程之前,会将ThreadList类的成员变量suspend_all_count_的值增加1。
接下来,ThreadList类的成员函数SuspendAll遍历所有的ART运行时线程,并且调用Thread类的成员函数ModifySuspendCount将它内部的线程计算数器增加1,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 void Thread::AtomicSetFlag(ThreadFlag flag) {
android_atomic_or(flag, &state_and_flags_.as_int); }
void Thread::AtomicClearFlag(ThreadFlag flag) {
android_atomic_and(-1 ^ flag, &state_and_flags_.as_int); } ......
void Thread::ModifySuspendCount(Thread* self, int delta, bool for_debugger) { ......
suspend_count_ += delta; ......
if (suspend_count_ == 0) {
AtomicClearFlag(kSuspendRequest); } else {
AtomicSetFlag(kSuspendRequest); } }
这三个函数定义在文件art/runtime/thread.cc中。
Thread类的成员函数ModifySuspendCount的实现很简单,它主要就是将成员变量suspend_count_的值增加delta,并且判断增加后的值是否等于0。如果等于0,就调用成员函数AtomicClearFlag将另外一个成员变量state_and_flags_的int值的kSuspendRequest位清0,表示线程没有挂起请求。否则的话,就调用成员函数AtomicSetFlag将成员变量state_and_flags_的int值的kSuspendRequest位置1,表示线程有挂起请求。
回到前面ThreadList类的成员函数SuspendAll中,全局ART运行时线程挂起计数器和每一个ART运行时线程内部的线程挂起计数器的操作都是需要在获取Locks类的静态成员变量thread_suspend_count_lock_描述的一个互斥锁的前提下进行的。
最后,ThreadList类的成员函数SuspendAll通过获取Locks类的静态成员变量mutator_lock_描述的一个读写锁的写访问来等待所有的ART运行时线程挂起的。这是如何做到的呢?在前面一文中,我们提到,ART运行时提供给由DEX字节码翻译而来的本地机器代码使用的一个函数表中,包含了一个pCheckSuspend函数指针,该函数指针指向了函数CheckSuspendFromCode。于是,每一个ART运行时线程在执行本地机器代码的过程中,就会周期性地通过调用函数CheckSuspendFromCode来检查自己是否需要挂起。这一点与前面一文分析的Dalvik虚拟机线程挂起的过程是类似的。
函数CheckSuspendFromCode的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 void CheckSuspendFromCode(Thread* thread)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { ......
CheckSuspend(thread); }
这个函数定义在文件art/runtime/entrypoints/quick/quick_thread_entrypoints.cc中。 函数CheckSuspendFromCode调用另外一个函数CheckSuspend检查当前线程是否需要挂起,后者的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 static inline void CheckSuspend(Thread* thread) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { for (;;) {
共分享92篇相关文档