ART虚拟机中,CardTable可看作是一个元素大小为1字节的数组,该数组的每一个元素叫作一个Card。 AllocatedSpace 按 128 字节(可更改)大小进行划分。每一个 Card 对应为Space中的一个128 字节区域。 所以, 凡是内存 地址位于某一个 128 字节 区域的 Object 对象 都 对应 同一个 Card。 在 这个 128 字节 区域 内 的 Object 对象 中, 只要 有一个 引用 了 新生代的对象, 则 该区 域 对应 的 Card 元素 将被 标记 为 脏 Card( Dirty Card)。 CardTable 是以 128 字节 空间为管理对象。这 128 字节的空间中可能存在多个Object。
ART虚拟机中只要给某个对象的引用型成员变量设置非零值,该对象对应的card将被标记为dirtycard。从某种意义上说,dirtycard可以作为追踪对象的一种手段。比如,某card的值为0,说明该card对应的对象的引用型成员变量没有被设置过。在标记的时候就不需要遍历这个对象的引用型成员变量。
art/runtime/gc/heap.cc-> Heap::Heap
static constexpr size_t kMinHeapAddress = 4 * KB;
card_table_.reset(accounting::CardTable::Create(reinterpret_cast<uint8_t*>(kMinHeapAddress),
4 * GB - kMinHeapAddress));
card_table_所能记录的最大内存范围为4GB左右,知art虚拟机堆中连续内存的大小是有限制的。
class CardTable {
static constexpr size_t kCardShift = 10;
static constexpr size_t kCardSize = 1 << kCardShift;
static constexpr uint8_t kCardClean = 0x0;
static constexpr uint8_t kCardDirty = 0x70;
static constexpr uint8_t kCardAged = kCardDirty - 1;
// Mmapped pages for the card table
MemMap mem_map_;
// Value used to compute card table addresses from object addresses, see GetBiasedBegin
uint8_t* const biased_begin_;
// Card table doesn't begin at the beginning of the mem_map_, instead it is displaced by offset
// to allow the byte value of `biased_begin_` to equal `kCardDirty`.
const size_t offset_;
};
创建
(1) CardTable保存信息的内存空间mem_map
(2) 计算mem_map和堆起始地址的偏移
(3) biased_begin后两位需要为0x70,offset记录biased_begin更改后的差值
CardTable* CardTable::Create(const uint8_t* heap_begin, size_t heap_capacity) {
/* Set up the card table */
size_t capacity = heap_capacity / kCardSize;
/* Allocate an extra 256 bytes to allow fixed low-byte of base */
std::string error_msg;
//(1)
MemMap mem_map = MemMap::MapAnonymous("card table",
capacity + 256,
PROT_READ | PROT_WRITE,
/*low_4gb=*/ false,
&error_msg);
CHECK(mem_map.IsValid()) << "couldn't allocate card table: " << error_msg;
// All zeros is the correct initial value; all clean. Anonymous mmaps are initialized to zero, we
// don't clear the card table to avoid unnecessary pages being allocated
static_assert(kCardClean == 0, "kCardClean must be 0");
uint8_t* cardtable_begin = mem_map.Begin();
// We allocated up to a bytes worth of extra space to allow `biased_begin`'s byte value to equal
// `kCardDirty`, compute a offset value to make this the case
size_t offset = 0;
//(2)
uint8_t* biased_begin = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(cardtable_begin) -
(reinterpret_cast<uintptr_t>(heap_begin) >> kCardShift));
uintptr_t biased_byte = reinterpret_cast<uintptr_t>(biased_begin) & 0xff;
if (biased_byte != kCardDirty) {
int delta = kCardDirty - biased_byte;
//(3)
offset = delta + (delta < 0 ? 0x100 : 0);
biased_begin += offset;
}
CHECK_EQ(reinterpret_cast<uintptr_t>(biased_begin) & 0xff, kCardDirty);
return new CardTable(std::move(mem_map), biased_begin, offset);
}
常用的函数
MarkCard
ALWAYS_INLINE void MarkCard(const void *addr) {
*CardFromAddr(addr) = kCardDirty;
}
CardFromAddr 用于根据某个Object对象的地址找到对应的Card 地址。
inline uint8_t* CardTable::CardFromAddr(const void *addr) const {
uint8_t *card_addr = biased_begin_ + (reinterpret_cast<uintptr_t>(addr) >> kCardShift);
return card_addr;
}
IsDirty
判断对象引用的成员变量是否被设置
bool IsDirty(const mirror::Object* obj) const {
return GetCard(obj) == kCardDirty;
}
uint8_t GetCard(const mirror::Object* obj) const {
return *CardFromAddr(obj);
}
RememberedSet
class RememberedSet {
public:
typedef std::set<uint8_t*, std::less<uint8_t*>,
TrackingAllocator<uint8_t*, kAllocatorTagRememberedSet>> CardSet;
explicit RememberedSet(const std::string& name, Heap* heap, space::ContinuousSpace* space)
: name_(name), heap_(heap), space_(space) {}
// Clear dirty cards and add them to the dirty card set.
void ClearCards();
// Mark through all references to the target space.
void UpdateAndMarkReferences(space::ContinuousSpace* target_space,
collector::GarbageCollector* collector)
REQUIRES(Locks::heap_bitmap_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
void AssertAllDirtyCardsAreWithinSpace() const;
private:
const std::string name_;
Heap* const heap_;
space::ContinuousSpace* const space_;
CardSet dirty_cards_;
};
ClearCards
void RememberedSet::ClearCards() {
CardTable* card_table = GetHeap()->GetCardTable();
RememberedSetCardVisitor card_visitor(&dirty_cards_);
// Clear dirty cards in the space and insert them into the dirty card set.
card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), card_visitor);
}
AgeCardVisitor
class AgeCardVisitor {
public:
uint8_t operator()(uint8_t card) const {
return (card == accounting::CardTable::kCardDirty) ? card - 1 : 0;
}
};
class RememberedSetCardVisitor {
public:
explicit RememberedSetCardVisitor(RememberedSet::CardSet* const dirty_cards)
: dirty_cards_(dirty_cards) {}
void operator()(uint8_t* card, uint8_t expected_value, uint8_t new_value ATTRIBUTE_UNUSED) const {
if (expected_value == CardTable::kCardDirty) {
dirty_cards_->insert(card);
}
}
private:
RememberedSet::CardSet* const dirty_cards_;
};
RemeberedSet ClearCards清除space_对应card的kDirtyCard标志,并将旧值为kDirtyCard的card地址保存在dirty_cards_容器中。
UpdateAndMarkReferences
(1) 访问这128字节中的Object,每得到一个Object,就调用一次obj_visitor。
(2) 如果这128字节中的Object没有引用target_space的Object,则对应的card区域需要从dirty_cards容器中移除。
(3) 先将这个card存到临时容器remove_card_set中,后续将一次性移除。
void RememberedSet::UpdateAndMarkReferences(space::ContinuousSpace* target_space,
collector::GarbageCollector* collector) {
CardTable* card_table = heap_->GetCardTable();
bool contains_reference_to_target_space = false;
RememberedSetObjectVisitor obj_visitor(target_space, &contains_reference_to_target_space,
collector);
ContinuousSpaceBitmap* bitmap = space_->GetLiveBitmap();
CardSet remove_card_set;
for (uint8_t* const card_addr : dirty_cards_) {
contains_reference_to_target_space = false;
uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
DCHECK(space_->HasAddress(reinterpret_cast<mirror::Object*>(start)));
//(1)
bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, obj_visitor);
//(2)
if (!contains_reference_to_target_space) {
// It was in the dirty card set, but it didn't actually contain
// a reference to the target space. So, remove it from the dirty
// card set so we won't have to scan it again (unless it gets
// dirty again.)
remove_card_set.insert(card_addr);
}
}
// Remove the cards that didn't contain a reference to the target
// space from the dirty card set.
//(3)
for (uint8_t* const card_addr : remove_card_set) {
DCHECK(dirty_cards_.find(card_addr) != dirty_cards_.end());
dirty_cards_.erase(card_addr);
}
}
class RememberedSetObjectVisitor {
public:
RememberedSetObjectVisitor(space::ContinuousSpace* target_space,
bool* const contains_reference_to_target_space,
collector::GarbageCollector* collector)
: collector_(collector), target_space_(target_space),
contains_reference_to_target_space_(contains_reference_to_target_space) {}
void operator()(ObjPtr<mirror::Object> obj) const REQUIRES(Locks::heap_bitmap_lock_)
REQUIRES_SHARED(Locks::mutator_lock_) {
RememberedSetReferenceVisitor visitor(target_space_, contains_reference_to_target_space_,
collector_);
obj->VisitReferences(visitor, visitor);
}
private:
collector::GarbageCollector* const collector_;
space::ContinuousSpace* const target_space_;
bool* const contains_reference_to_target_space_;
};
class RememberedSetReferenceVisitor {
public:
RememberedSetReferenceVisitor(space::ContinuousSpace* target_space,
bool* const contains_reference_to_target_space,
collector::GarbageCollector* collector)
: collector_(collector), target_space_(target_space),
contains_reference_to_target_space_(contains_reference_to_target_space) {}
void operator()(ObjPtr<mirror::Object> obj,
MemberOffset offset,
bool is_static ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(obj != nullptr);
mirror::HeapReference<mirror::Object>* ref_ptr = obj->GetFieldObjectReferenceAddr(offset);
if (target_space_->HasAddress(ref_ptr->AsMirrorPtr())) {
*contains_reference_to_target_space_ = true;
collector_->MarkHeapReference(ref_ptr, /*do_atomic_update=*/ false);
DCHECK(!target_space_->HasAddress(ref_ptr->AsMirrorPtr()));
}
}
void operator()(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Reference> ref) const
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
if (target_space_->HasAddress(ref->GetReferent())) {
*contains_reference_to_target_space_ = true;
collector_->DelayReferenceReferent(klass, ref);
}
}
void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
REQUIRES_SHARED(Locks::mutator_lock_) {
if (!root->IsNull()) {
VisitRoot(root);
}
}
void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
REQUIRES_SHARED(Locks::mutator_lock_) {
if (target_space_->HasAddress(root->AsMirrorPtr())) {
*contains_reference_to_target_space_ = true;
root->Assign(collector_->MarkObject(root->AsMirrorPtr()));
DCHECK(!target_space_->HasAddress(root->AsMirrorPtr()));
}
}
private:
collector::GarbageCollector* const collector_;
space::ContinuousSpace* const target_space_;
bool* const contains_reference_to_target_space_;
};
在Heap构造函数中,为每个ImageSpace对象创建了一个ModUnionTableToZygoteAllocspace对象。
art/runtime/gc/heap.cc:676 ->Heap:Heap
if (HasBootImageSpace()) {
// Don't add the image mod union table if we are running without an image, this can crash if
// we use the CardCache implementation.
for (space::ImageSpace* image_space : GetBootImageSpaces()) {
accounting::ModUnionTable* mod_union_table = new accounting::ModUnionTableToZygoteAllocspace(
"Image mod-union table", this, image_space);
CHECK(mod_union_table != nullptr) << "Failed to create image mod-union table";
AddModUnionTable(mod_union_table);
}
}