virtual GcType GetGcType() const OVERRIDE {
return kGcTypePartial;//MarkCompact不处理ImageSpace和ZygoteSpace
}
virtual CollectorType GetCollectorType() const OVERRIDE {
return kCollectorTypeMC;
}
void MarkCompact::SetSpace(space::BumpPointerSpace* space) {
space_ = space;//space_是MarkCompact成员变量
}
void MarkCompact::RunPhases() {
Thread* self = Thread::Current();
/* InitializePhase非常简单,其中需要注意的是MarkCompact下面两个成员变量的设置:
(1) mark_stack_ = heap_->GetMarkStack();
(2) mark_bitmap_ = heap_->GetMarkBitmap();
*/
InitializePhase();
{
ScopedPause pause(this);//MarkCompact是stop-the-world类型的回收器
......
MarkingPhase();//①标记阶段,详情见下文分析
ReclaimPhase();//②回收阶段,详情见下文分析
}
......
FinishPhase();//收尾工作,非常简单,读者可自行阅读
}
void MarkCompact::MarkingPhase() {
Thread* self = Thread::Current();
/*MarkCompact基于Mark-Compact回收原理,所以它也需要标记能搜索到的对象。不过,由于
BumpPointerSpace空间对象不包含位图对象,所以下面将为space_(指向一个
BumpPointerSpace空间)创建一个位图对象objects_before_forwarding。它用于记录
搜索到的对象。*/
objects_before_forwarding_.reset(accounting::ContinuousSpaceBitmap::Create(
"objects before forwarding", space_->Begin(), space_->Size()));
//此外还创建了一个位图对象objects_with_lockword_,它和GC没有什么关系,只是用于
//保存一些信息。下文将见到objects_with_lockword_的作用
objects_with_lockword_.reset(accounting::ContinuousSpaceBitmap::Create(
"objects with lock words", space_->Begin(), space_->Size()));
//将ImageSpace或ZygoteSpace加到MarkCompact immune_space_容器中
BindBitmaps();
/*ProcessCards和ClearCardTable用于处理CardTable中对应的card。此处请读者注意,
虽然MarkCompact也是通过移动对象来实现内存回收,但MarkCompact移动对象的过程是在
最后的回收阶段。此时,所有的非垃圾对象都已经标记。所以,MarkCompact中可以使用Write
Barrier来记录跨空间的对象引用。作为对比,ConcurrentCopying在标记阶段可能就会移动
对象,这时就不方便使用Write Barrier了,而只能使用Read Barrier。 */
heap_->ProcessCards(GetTimings(), false, false, true);
heap_->GetCardTable()->ClearCardTable();
//下面几个函数我们都介绍过
if (kUseThreadLocalAllocationStack) {
......
heap_->RevokeAllThreadLocalAllocationStacks(self);
}
......
heap_->SwapStacks();
{
WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
MarkRoots();//搜索并标记根对象,详情见下文
//借助CardTable来处理ImageSpace或ZygoteSpace中存在跨空间引用的对象,每找到
//这样一个对象就对其做标记并压入mark_stack_中。读者可先了解上面的MarkRoots函数
UpdateAndMarkModUnion();
MarkReachableObjects();//从根对象出发,扫描它们所引用的对象
}
......//Java Reference对象的处理等
}
void MarkCompact::ReclaimPhase() {
......
WriterMutexLock mu(Thread::Current(), *Locks::heap_bitmap_lock_);
/*Sweep将回收除space_、immune_spaces_外其他空间对象中的垃圾。内部代码逻辑非常简单,
就是调用ContinuousMemMapAllocSpace的Sweep函数进行回收。这些空间的垃圾回收使用
的是Mark-Sweep方法,不是Mark-Compact。*/
Sweep(false);
//调用GarbageCollector SwapBitmaps函数,该函数在MarkSweep类中已经介绍过了
SwapBitmaps();
GetHeap()->UnBindBitmaps();//该函数在MarkSweep中已经介绍过了
//压缩,这才是MarkCompact的精髓
Compact();
}
graph LR
CalculateObjectForwardingAddresses-->UpdateReferences-->MoveObjectsToForwardingAddress
void MarkCompact::Compact() {
.....
/*Compact中有三个关键函数,此处先简单介绍它们的作用:
1: CalculateObjectForwardingAddresses:计算每个存活对象的forwarding address。
这个地址也就是这些对象的新的内存地址。
2: UpdateReferences:更新对象的引用关系,将所引用的对象修改为对应的forwarding
address。这个函数没有什么特殊的知识,读者可自行阅读。
3: MoveObjects:将对象移动到它的forwarding address处。 */
CalculateObjectForwardingAddresses();
UpdateReferences();
MoveObjects();
......
/*更新space_的末尾位置。经过上面压缩处理后,space_中的垃圾对象被清除,而非垃圾对象们
又被移动到了一起。这些非垃圾对象在space_中的末尾位置由bump_pointer_标示。 */
space_->SetEnd(bump_pointer_);
//清零[bump_Pointer_,bump_pointer_+bytes_freed)这段空间。这段空间就是垃圾对象所
//占据的内存大小
memset(bump_pointer_, 0, bytes_freed);
}
//CalculateObjectForwardingAddress展示了MarkCompact中Compact的方法,就是将非垃圾对象一个一个排列起来。显然,要支持这种操作的话非BumpPointerSpace不可。
void MarkCompact::CalculateObjectForwardingAddresses() {
......
//bump_pointer_初值为space_的起始位置
bump_pointer_ = reinterpret_cast<uint8_t*>(space_->Begin());
/*objects_before_forwarding_记录了space_中非垃圾对象的位图信息。下面的代码
将遍历space_中的非垃圾对象,然后调用函数对象进行处理。
CalculateObjectForwardingAddressVisitor内部调用MarkCompact ForwardObject
对每一个非垃圾对象进行处理。我们直接来看ForwardObject。*/
CalculateObjectForwardingAddressVisitor visitor(this);
objects_before_forwarding_->VisitMarkedRange(
reinterpret_cast<uintptr_t>(space_->Begin()),
reinterpret_cast<uintptr_t>(space_->End()),
visitor);
}
void MarkCompact::ForwardObject(mirror::Object* obj) {
//获取这个对象的所占内存的大小
const size_t alloc_size = RoundUp(obj->SizeOf(), space::BumpPointerSpace::kAlignment);
LockWord lock_word = obj->GetLockWord(false);
/*如果这个obj之前有设置LockWord(可能代表一个用于线程同步的Monitor),下面的if代码将
把LockWord旧值保存起来。等后续对象移动完毕后,我们需要恢复Obj的LockWord的旧值。 */
if (!LockWord::IsDefault(lock_word)) {
//objects_with_lockword_记录哪个对象存在LockWord的旧值
objects_with_lockword_->Set(obj);
//lock_words_to_restore_是一个stddqueue(双端队列),用于保存obj的
//LockWord旧值
lock_words_to_restore_.push_back(lock_word);
}
//设置obj的forwarding address,为bump_pointer_
obj->SetLockWord(LockWord::FromForwardingAddress(reinterpret_cast<size_t>(bump_pointer_)),false);
//移动bump_poionter_,使得它指向下一个对象的forwarding address
bump_pointer_ += alloc_size;
++live_objects_in_space_;
}
……
void MarkCompact::MoveObjects() {
......
/*遍历存活对象,MoveObjectVisitor内部调用MoveObject函数进行处理,下面将直接介绍
MoveObject的内容。*/
MoveObjectVisitor visitor(this);//内部调用MoveObject
objects_before_forwarding_->VisitMarkedRange(
reinterpret_cast<uintptr_t>(space_->Begin()),
reinterpret_cast<uintptr_t>(space_->End()),
visitor);
......
}
void MarkCompact::MoveObject(mirror::Object* obj, size_t len) {
//从LockWord中获取obj的目标地址
uintptr_t dest_addr = obj->GetLockWord(false).ForwardingAddress();
mirror::Object* dest_obj = reinterpret_cast<mirror::Object*>(dest_addr);
//使用memmove将obj移动到dest_addr处。
memmove(reinterpret_cast<void*>(dest_addr),
reinterpret_cast<const void*>(obj), len);
LockWord lock_word = LockWord::Default();
//如果obj之前有LockWord旧值,则需要从lock_words_to_restore_中拿到旧值
if (UNLIKELY(objects_with_lockword_->Test(obj))) {
lock_word = lock_words_to_restore_.front();
lock_words_to_restore_.pop_front();
}
//设置dest_obj的LockWord。
dest_obj->SetLockWord(lock_word, false);
}