分配中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 自行查看