云题海 - 专业文章范例文档资料分享平台

当前位置:首页 > ART运行时垃圾收集(GC)过程分析

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

  • 62 次阅读
  • 3 次下载
  • 2026/4/23 12:25:11

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 (;;) {

搜索更多关于: ART运行时垃圾收集(GC)过程分析 的文档
  • 收藏
  • 违规举报
  • 版权认领
下载文档10.00 元 加入VIP免费下载
推荐下载
本文作者:...

共分享92篇相关文档

文档简介:

1. 调用子类实现的成员函数InitializePhase执行GC初始化阶段。 2. 挂起所有的ART运行时线程。 3. 调用子类实现的成员函数MarkingPhase执行GC标记阶段。 4. 调用子类实现的成员函数ReclaimPhase执行GC回收阶段。 5. 恢复第2步挂起的ART运行时线程。 6. 调用子类实现的成员函数FinishPhase执行GC结束阶段。 图1所示的右边流程是用来执行并行GC的,过程如下所示: 1. 调用子类实现的成员函数InitializePhase执行GC初始化阶段。 2. 获取用于

× 游客快捷下载通道(下载后可以自由复制和排版)
单篇付费下载
限时特价:10 元/份 原价:20元
VIP包月下载
特价:29 元/月 原价:99元
低至 0.3 元/份 每月下载150
全站内容免费自由复制
VIP包月下载
特价:29 元/月 原价:99元
低至 0.3 元/份 每月下载150
全站内容免费自由复制
注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信:fanwen365 QQ:370150219
Copyright © 云题海 All Rights Reserved. 苏ICP备16052595号-3 网站地图 客服QQ:370150219 邮箱:370150219@qq.com