(三) 对象之间引用

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);
    }
  }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 参考:罗升阳的相关博客https://blog.csdn.net/Luoshengyang/article/det...
    奋飞的蜗牛ing阅读 958评论 0 0
  • 1、从编码到执行 解释执行和编译执行是可以混合的,执行次数多的代码,会进行 JIT 的编译,交由操作系统直接执行。...
    ArthurHC阅读 528评论 0 2
  • Catalog 1 怎么解决OOM?/ 怎么排查OOM?/ JVM调优1.1 JDK自带工具1.2 阿里开源JVM...
    allen锅阅读 516评论 0 1
  • 三色标记法是一种垃圾回收法,它可以让JVM不发生或仅短时间发生STW(Stop The World),从而达到清除...
    Gino_4bd4阅读 1,641评论 0 2
  • 3:类加载-初始化 1. 加载过程 1. Loading 1. 双亲委派,主要出于安全来考虑 2. LazyLoa...
    Yuszha阅读 557评论 0 0

友情链接更多精彩内容