垃圾回收原理-流程篇

分配中gc

今天我们来接着之前内存相关的文章来看看Android ART虚拟机垃圾回收的流程。之前我分享过 Android 内存分配过程中,如果内存不足会发生gc:

图片

这里会调用 heap.cc 里的 CollectGarbageInternal 方法。返回了本次gc的类型。

collector::GcType Heap::CollectGarbageInternal(    collector::GcType gc_type,    GcCause gc_cause,    bool clear_soft_references,    uint32_t requested_gc_num)

GcType 枚举对应下面这几种:

enum GcType {  // Placeholder for when no GC has been performed.  kGcTypeNone,  // Sticky mark bits GC that attempts to only free objects allocated since the last GC.  kGcTypeSticky,  // Partial GC that marks the application heap but not the Zygote.  kGcTypePartial,  // Full GC that marks and frees in both the application and Zygote heap.  kGcTypeFull,};

分别是不执行gc、回收本次分配的对象、回收ZtgoteSpace之外的对象、完整的回收。

这个方法还是比较长的,我按执行的步骤分部贴一下主要代码,主要步骤我这里就划分为

  • 判断是否执行gc

  • 确定收集器类型

  • 执行gc

  • 整理堆空间

  • 结束gc

判断是否执行
switch (gc_type) {    case collector::kGcTypePartial: {      if (!HasZygoteSpace()) {        return collector::kGcTypeNone;      }      break;    }    default: {    }}ScopedThreadStateChange tsc(self, ThreadState::kWaitingPerformingGc);Locks::mutator_lock_->AssertNotHeld(self);if (self->IsHandlingStackOverflow()) {  gcs_completed_.fetch_add(1, std::memory_order_release);  return collector::kGcTypeNone;}

这里是完全没条件执行gc,所以返回 kGcTypeNone。

MutexLock mu(self, *gc_complete_lock_);WaitForGcToCompleteLocked(gc_cause, self);if (requested_gc_num != GC_NUM_ANY && !GCNumberLt(GetCurrentGcNum(), requested_gc_num)) {  return collector::kGcTypeNone;}

同一时间只能有一个gc在执行。

compacting_gc = IsMovingGc(collector_type_);if (compacting_gc && disable_moving_gc_count_ != 0) {  gcs_completed_.fetch_add(1, std::memory_order_release);  return collector::kGcTypeNone;}if (gc_disabled_for_shutdown_) {  cs_completed_.fetch_add(1, std::memory_order_release);  return collector::kGcTypeNone;}

gc被禁用。

compacting_gc

这里有一个 compacting_gc 的判断,我们看下他的具体实现:

图片

当垃圾回收算法是 CC(并发复制)的时候,compactiing_gc 是true,从 compacting 这个词加上过去一些垃圾回收算法八股文的学习,我们可以推测,这个代表的应该是是否移动对象,压缩碎片空间的意思。

确定收集器类型

如果满足gc执行条件,那么需要确定一下当前的收集器类型。

图片

如果是并发拷贝:

if (use_generational_cc_) {  active_cc_collector = (gc_type == collector::kGcTypeSticky) ? young_concurrent_copying_collector_ : concurrent_copying_collector_;  active_concurrent_copying_collector_.store(active_cc_collector,std::memory_order_relaxed);  collector = active_cc_collector;} else {  collector = active_concurrent_copying_collector_.load(std::memory_order_relaxed);}

这里根据分代回收的配置来确定cc的收集器:

图片

这2个都是 ConcurrentCopying 这个收集器,但是创建的时候 yong_gen 参数不一样。

如果collector_type_是CMS(并发标记清理)的,那么会执行下面这个条件:

else if (current_allocator_ == kAllocatorTypeRosAlloc ||  current_allocator_ == kAllocatorTypeDlMalloc) {  collector = FindCollectorByGcType(gc_type);}

这里的收集器类型一般为 RosAlloc 或者 DlMalloc。

图片

f

会从 garbage_collectors 查找,garbage_collectors 里面一般是MarkSweep、PartialMarkSweep、StickyMarkSweep、SemiSpace 这几个收集器。他们定义的关系我列一下:

  • MarkSweep:  kGcTypeFull + kCollectorTypeCMS

  • PartialMarkSweep: kGcTypePartial + kCollectorTypeCMS

  • StickyMarkSweep: kGcTypeSticky + kCollectorTypeCMS

  • SemiSpace: kGcTypePartial + kCollectorTypeSS

那么如果是 CMS 的,那么基本对应 MarkSweep 打头的这几个,而且这几个类的关系上也是继承关系:

(ps: 图方便随便画了一下继承关系,图里的箭头是父类指向了子类,不要理解错了)

图片

执行垃圾回收

确定收集器之后执行gc,调用的都是GarbageCollector的 Run 方法:

collector->Run(gc_cause, clear_soft_references || runtime->IsZygote());

Run里面执行 RunPhases,这个方法就是每个收集器子类自己实现了。具体实现以后再分享。

图片

整理堆空间

调用GrowForUtilization方法整理堆空间,这里面没什么重要的内容,最核心的地方就是根据gc类型来调整gc的触发阈值 concurrent_start_bytes:

图片

结束gc

结束gc,返回本次gc类型。

FinishGC(self, gc_type);clear->Run(self);clear->Finalize();Dbg::GcDidFinish();return gc_type;
阈值触发gc

分析完分配中gc,我还注意到分配完成后也会再做一次内存大小检查,来决定是否触发gc,逻辑在 heap-inl.h 的 AllocObjectWithAllocator 函数:

图片

图片

这个地方就是和上文提到的阈值 concurrent_start_bytes__ _做对比。

如果需要gc,会调用 RequestConcurrentGCAndSaveObject 方法,进而调用 RequestConcurrentGC:

图片

这里会给 task_processor_  添加一个 ConcurrentGCTask, 他的Run方法会调 heap 的 ConcurrentGC:

图片

最后仍然调用了CollectGarbageInternal 进行gc,所以CollectGarbageInternal 也是 ART虚拟机里垃圾回收的入口。

其他gc原因

CollectGarbageInternal 的参数里面有一个GcCause枚举,回溯前面2个case的代码,使用的case分别是 kGcCauseForAlloc 和 kGcCauseBackground。其他一些类型的cause我也随便列几个,但是就不一一分析了。实际上最常见的就是内存分配不足、内存到达gc阈值水平这两种。

  • kGcCauseExplicit: java层调用System.gc()

  • kGcCauseForNativeAlloc: NativeAllocationGcWatermark 超出的时候调用的,这个涉及到native分配的机制,感兴趣的也可以研究一下

  • kGcCauseCollectorTransition: 修改垃圾收集器类型

  • ..... 太多了,可以去 https://cs.android.com/android/platform/superproject/main/+/main:art/runtime/gc/gc_cause.h?q=gc_cause 自行查看

转自:Android垃圾回收原理-流程篇

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值