分配中gc
今天我们来接着之前内存相关的文章来看看Android ART虚拟机垃圾回收的流程。之前我分享过 Android 内存分配过程中,如果内存不足会发生gc:
这里会调用 heap.cc 里的 CollectGarbageInternal 方法。返回了本次gc的类型。
plain代码解读复制代码collector::GcType Heap::CollectGarbageInternal( collector::GcType gc_type, GcCause gc_cause, bool clear_soft_references, uint32_t requested_gc_num )
GcType 枚举对应下面这几种:
plain代码解读复制代码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
判断是否执行
plain代码解读复制代码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。
plain代码解读复制代码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在执行。
plain代码解读复制代码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执行条件,那么需要确定一下当前的收集器类型。
如果是并发拷贝:
plain代码解读复制代码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(并发标记清理)的,那么会执行下面这个条件:
plain代码解读复制代码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 方法:
plain代码解读复制代码collector->Run(gc_cause, clear_soft_references || runtime->IsZygote());
Run里面执行 RunPhases,这个方法就是每个收集器子类自己实现了。具体实现以后再分享。
整理堆空间
调用GrowForUtilization方法整理堆空间,这里面没什么重要的内容,最核心的地方就是根据gc类型来调整gc的触发阈值 concurrent_start_bytes:
结束gc
结束gc,返回本次gc类型。
plain代码解读复制代码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: 修改垃圾收集器类型
- ..... 太多了,可以去 cs.android.com/android/pla… 自行查看
评论记录:
回复评论: