Home | History | Annotate | Download | only in accounting
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "mod_union_table.h"
     18 
     19 #include <memory>
     20 
     21 #include "base/stl_util.h"
     22 #include "bitmap-inl.h"
     23 #include "card_table-inl.h"
     24 #include "gc/accounting/space_bitmap-inl.h"
     25 #include "gc/heap.h"
     26 #include "gc/space/image_space.h"
     27 #include "gc/space/space.h"
     28 #include "mirror/object-inl.h"
     29 #include "mirror/object-refvisitor-inl.h"
     30 #include "object_callbacks.h"
     31 #include "space_bitmap-inl.h"
     32 #include "thread-current-inl.h"
     33 
     34 namespace art {
     35 namespace gc {
     36 namespace accounting {
     37 
     38 class ModUnionAddToCardSetVisitor {
     39  public:
     40   explicit ModUnionAddToCardSetVisitor(ModUnionTable::CardSet* const cleared_cards)
     41       : cleared_cards_(cleared_cards) {}
     42 
     43   inline void operator()(uint8_t* card,
     44                          uint8_t expected_value,
     45                          uint8_t new_value ATTRIBUTE_UNUSED) const {
     46     if (expected_value == CardTable::kCardDirty) {
     47       cleared_cards_->insert(card);
     48     }
     49   }
     50 
     51  private:
     52   ModUnionTable::CardSet* const cleared_cards_;
     53 };
     54 
     55 class ModUnionAddToCardBitmapVisitor {
     56  public:
     57   ModUnionAddToCardBitmapVisitor(ModUnionTable::CardBitmap* bitmap, CardTable* card_table)
     58       : bitmap_(bitmap), card_table_(card_table) {}
     59 
     60   inline void operator()(uint8_t* card,
     61                          uint8_t expected_value,
     62                          uint8_t new_value ATTRIBUTE_UNUSED) const {
     63     if (expected_value == CardTable::kCardDirty) {
     64       // We want the address the card represents, not the address of the card.
     65       bitmap_->Set(reinterpret_cast<uintptr_t>(card_table_->AddrFromCard(card)));
     66     }
     67   }
     68 
     69  private:
     70   ModUnionTable::CardBitmap* const bitmap_;
     71   CardTable* const card_table_;
     72 };
     73 
     74 class ModUnionAddToCardVectorVisitor {
     75  public:
     76   explicit ModUnionAddToCardVectorVisitor(std::vector<uint8_t*>* cleared_cards)
     77       : cleared_cards_(cleared_cards) {
     78   }
     79 
     80   void operator()(uint8_t* card, uint8_t expected_card, uint8_t new_card ATTRIBUTE_UNUSED) const {
     81     if (expected_card == CardTable::kCardDirty) {
     82       cleared_cards_->push_back(card);
     83     }
     84   }
     85 
     86  private:
     87   std::vector<uint8_t*>* const cleared_cards_;
     88 };
     89 
     90 class ModUnionUpdateObjectReferencesVisitor {
     91  public:
     92   ModUnionUpdateObjectReferencesVisitor(MarkObjectVisitor* visitor,
     93                                         space::ContinuousSpace* from_space,
     94                                         space::ContinuousSpace* immune_space,
     95                                         bool* contains_reference_to_other_space)
     96     : visitor_(visitor),
     97       from_space_(from_space),
     98       immune_space_(immune_space),
     99       contains_reference_to_other_space_(contains_reference_to_other_space) {}
    100 
    101   // Extra parameters are required since we use this same visitor signature for checking objects.
    102   void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
    103       REQUIRES_SHARED(Locks::mutator_lock_) {
    104     MarkReference(obj->GetFieldObjectReferenceAddr(offset));
    105   }
    106 
    107   void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
    108       REQUIRES_SHARED(Locks::mutator_lock_) {
    109     VisitRoot(root);
    110   }
    111 
    112   void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
    113       REQUIRES_SHARED(Locks::mutator_lock_) {
    114     MarkReference(root);
    115   }
    116 
    117  private:
    118   template<bool kPoisonReferences>
    119   void MarkReference(mirror::ObjectReference<kPoisonReferences, mirror::Object>* obj_ptr) const
    120       REQUIRES_SHARED(Locks::mutator_lock_) {
    121     // Only add the reference if it is non null and fits our criteria.
    122     mirror::Object* ref = obj_ptr->AsMirrorPtr();
    123     if (ref != nullptr && !from_space_->HasAddress(ref) && !immune_space_->HasAddress(ref)) {
    124       *contains_reference_to_other_space_ = true;
    125       mirror::Object* new_object = visitor_->MarkObject(ref);
    126       if (ref != new_object) {
    127         obj_ptr->Assign(new_object);
    128       }
    129     }
    130   }
    131 
    132   MarkObjectVisitor* const visitor_;
    133   // Space which we are scanning
    134   space::ContinuousSpace* const from_space_;
    135   space::ContinuousSpace* const immune_space_;
    136   // Set if we have any references to another space.
    137   bool* const contains_reference_to_other_space_;
    138 };
    139 
    140 class ModUnionScanImageRootVisitor {
    141  public:
    142   // Immune space is any other space which we don't care about references to. Currently this is
    143   // the image space in the case of the zygote mod union table.
    144   ModUnionScanImageRootVisitor(MarkObjectVisitor* visitor,
    145                                space::ContinuousSpace* from_space,
    146                                space::ContinuousSpace* immune_space,
    147                                bool* contains_reference_to_other_space)
    148       : visitor_(visitor),
    149         from_space_(from_space),
    150         immune_space_(immune_space),
    151         contains_reference_to_other_space_(contains_reference_to_other_space) {}
    152 
    153   void operator()(mirror::Object* root) const
    154       REQUIRES(Locks::heap_bitmap_lock_)
    155       REQUIRES_SHARED(Locks::mutator_lock_) {
    156     DCHECK(root != nullptr);
    157     ModUnionUpdateObjectReferencesVisitor ref_visitor(visitor_,
    158                                                       from_space_,
    159                                                       immune_space_,
    160                                                       contains_reference_to_other_space_);
    161     root->VisitReferences(ref_visitor, VoidFunctor());
    162   }
    163 
    164  private:
    165   MarkObjectVisitor* const visitor_;
    166   // Space which we are scanning
    167   space::ContinuousSpace* const from_space_;
    168   space::ContinuousSpace* const immune_space_;
    169   // Set if we have any references to another space.
    170   bool* const contains_reference_to_other_space_;
    171 };
    172 
    173 void ModUnionTableReferenceCache::ProcessCards() {
    174   CardTable* card_table = GetHeap()->GetCardTable();
    175   ModUnionAddToCardSetVisitor visitor(&cleared_cards_);
    176   // Clear dirty cards in the this space and update the corresponding mod-union bits.
    177   card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor);
    178 }
    179 
    180 void ModUnionTableReferenceCache::ClearTable() {
    181   cleared_cards_.clear();
    182   references_.clear();
    183 }
    184 
    185 class AddToReferenceArrayVisitor {
    186  public:
    187   AddToReferenceArrayVisitor(ModUnionTableReferenceCache* mod_union_table,
    188                              MarkObjectVisitor* visitor,
    189                              std::vector<mirror::HeapReference<mirror::Object>*>* references,
    190                              bool* has_target_reference)
    191       : mod_union_table_(mod_union_table),
    192         visitor_(visitor),
    193         references_(references),
    194         has_target_reference_(has_target_reference) {}
    195 
    196   // Extra parameters are required since we use this same visitor signature for checking objects.
    197   void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
    198       REQUIRES_SHARED(Locks::mutator_lock_) {
    199     mirror::HeapReference<mirror::Object>* ref_ptr = obj->GetFieldObjectReferenceAddr(offset);
    200     mirror::Object* ref = ref_ptr->AsMirrorPtr();
    201     // Only add the reference if it is non null and fits our criteria.
    202     if (ref != nullptr && mod_union_table_->ShouldAddReference(ref)) {
    203       // Push the adddress of the reference.
    204       references_->push_back(ref_ptr);
    205     }
    206   }
    207 
    208   void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
    209       REQUIRES_SHARED(Locks::mutator_lock_) {
    210     if (!root->IsNull()) {
    211       VisitRoot(root);
    212     }
    213   }
    214 
    215   void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
    216       REQUIRES_SHARED(Locks::mutator_lock_) {
    217     if (mod_union_table_->ShouldAddReference(root->AsMirrorPtr())) {
    218       *has_target_reference_ = true;
    219       // TODO: Add MarkCompressedReference callback here.
    220       mirror::Object* old_ref = root->AsMirrorPtr();
    221       mirror::Object* new_ref = visitor_->MarkObject(old_ref);
    222       if (old_ref != new_ref) {
    223         root->Assign(new_ref);
    224       }
    225     }
    226   }
    227 
    228  private:
    229   ModUnionTableReferenceCache* const mod_union_table_;
    230   MarkObjectVisitor* const visitor_;
    231   std::vector<mirror::HeapReference<mirror::Object>*>* const references_;
    232   bool* const has_target_reference_;
    233 };
    234 
    235 class ModUnionReferenceVisitor {
    236  public:
    237   ModUnionReferenceVisitor(ModUnionTableReferenceCache* const mod_union_table,
    238                            MarkObjectVisitor* visitor,
    239                            std::vector<mirror::HeapReference<mirror::Object>*>* references,
    240                            bool* has_target_reference)
    241       : mod_union_table_(mod_union_table),
    242         visitor_(visitor),
    243         references_(references),
    244         has_target_reference_(has_target_reference) {}
    245 
    246   void operator()(mirror::Object* obj) const
    247       REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
    248     // We don't have an early exit since we use the visitor pattern, an early
    249     // exit should significantly speed this up.
    250     AddToReferenceArrayVisitor visitor(mod_union_table_,
    251                                        visitor_,
    252                                        references_,
    253                                        has_target_reference_);
    254     obj->VisitReferences(visitor, VoidFunctor());
    255   }
    256 
    257  private:
    258   ModUnionTableReferenceCache* const mod_union_table_;
    259   MarkObjectVisitor* const visitor_;
    260   std::vector<mirror::HeapReference<mirror::Object>*>* const references_;
    261   bool* const has_target_reference_;
    262 };
    263 
    264 class CheckReferenceVisitor {
    265  public:
    266   CheckReferenceVisitor(ModUnionTableReferenceCache* mod_union_table,
    267                         const std::set<mirror::Object*>& references)
    268       : mod_union_table_(mod_union_table),
    269         references_(references) {}
    270 
    271   // Extra parameters are required since we use this same visitor signature for checking objects.
    272   void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
    273       REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
    274     mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
    275     if (ref != nullptr &&
    276         mod_union_table_->ShouldAddReference(ref) &&
    277         references_.find(ref) == references_.end()) {
    278       Heap* heap = mod_union_table_->GetHeap();
    279       space::ContinuousSpace* from_space = heap->FindContinuousSpaceFromObject(obj, false);
    280       space::ContinuousSpace* to_space = heap->FindContinuousSpaceFromObject(ref, false);
    281       LOG(INFO) << "Object " << reinterpret_cast<const void*>(obj) << "(" << obj->PrettyTypeOf()
    282                 << ")" << "References "
    283                 << reinterpret_cast<const void*>(ref) << "(" << mirror::Object::PrettyTypeOf(ref)
    284           << ") without being in mod-union table";
    285       LOG(INFO) << "FromSpace " << from_space->GetName() << " type "
    286           << from_space->GetGcRetentionPolicy();
    287       LOG(INFO) << "ToSpace " << to_space->GetName() << " type "
    288           << to_space->GetGcRetentionPolicy();
    289       heap->DumpSpaces(LOG_STREAM(INFO));
    290       LOG(FATAL) << "FATAL ERROR";
    291     }
    292   }
    293 
    294   void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
    295       REQUIRES_SHARED(Locks::mutator_lock_) {
    296     if (kIsDebugBuild && !root->IsNull()) {
    297       VisitRoot(root);
    298     }
    299   }
    300 
    301   void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
    302       REQUIRES_SHARED(Locks::mutator_lock_) {
    303     DCHECK(!mod_union_table_->ShouldAddReference(root->AsMirrorPtr()));
    304   }
    305 
    306  private:
    307   ModUnionTableReferenceCache* const mod_union_table_;
    308   const std::set<mirror::Object*>& references_;
    309 };
    310 
    311 class ModUnionCheckReferences {
    312  public:
    313   ModUnionCheckReferences(ModUnionTableReferenceCache* mod_union_table,
    314                           const std::set<mirror::Object*>& references)
    315       REQUIRES(Locks::heap_bitmap_lock_)
    316       : mod_union_table_(mod_union_table), references_(references) {}
    317 
    318   void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
    319     Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current());
    320     CheckReferenceVisitor visitor(mod_union_table_, references_);
    321     obj->VisitReferences(visitor, VoidFunctor());
    322   }
    323 
    324  private:
    325   ModUnionTableReferenceCache* const mod_union_table_;
    326   const std::set<mirror::Object*>& references_;
    327 };
    328 
    329 class EmptyMarkObjectVisitor : public MarkObjectVisitor {
    330  public:
    331   mirror::Object* MarkObject(mirror::Object* obj) OVERRIDE {return obj;}
    332   void MarkHeapReference(mirror::HeapReference<mirror::Object>*, bool) OVERRIDE {}
    333 };
    334 
    335 void ModUnionTable::FilterCards() {
    336   EmptyMarkObjectVisitor visitor;
    337   // Use empty visitor since filtering is automatically done by UpdateAndMarkReferences.
    338   UpdateAndMarkReferences(&visitor);
    339 }
    340 
    341 void ModUnionTableReferenceCache::Verify() {
    342   // Start by checking that everything in the mod union table is marked.
    343   for (const auto& ref_pair : references_) {
    344     for (mirror::HeapReference<mirror::Object>* ref : ref_pair.second) {
    345       CHECK(heap_->IsLiveObjectLocked(ref->AsMirrorPtr()));
    346     }
    347   }
    348 
    349   // Check the references of each clean card which is also in the mod union table.
    350   CardTable* card_table = heap_->GetCardTable();
    351   ContinuousSpaceBitmap* live_bitmap = space_->GetLiveBitmap();
    352   for (const auto& ref_pair : references_) {
    353     const uint8_t* card = ref_pair.first;
    354     if (*card == CardTable::kCardClean) {
    355       std::set<mirror::Object*> reference_set;
    356       for (mirror::HeapReference<mirror::Object>* obj_ptr : ref_pair.second) {
    357         reference_set.insert(obj_ptr->AsMirrorPtr());
    358       }
    359       ModUnionCheckReferences visitor(this, reference_set);
    360       uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
    361       live_bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, visitor);
    362     }
    363   }
    364 }
    365 
    366 void ModUnionTableReferenceCache::Dump(std::ostream& os) {
    367   CardTable* card_table = heap_->GetCardTable();
    368   os << "ModUnionTable cleared cards: [";
    369   for (uint8_t* card_addr : cleared_cards_) {
    370     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
    371     uintptr_t end = start + CardTable::kCardSize;
    372     os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << ",";
    373   }
    374   os << "]\nModUnionTable references: [";
    375   for (const auto& ref_pair : references_) {
    376     const uint8_t* card_addr = ref_pair.first;
    377     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
    378     uintptr_t end = start + CardTable::kCardSize;
    379     os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << "->{";
    380     for (mirror::HeapReference<mirror::Object>* ref : ref_pair.second) {
    381       os << reinterpret_cast<const void*>(ref->AsMirrorPtr()) << ",";
    382     }
    383     os << "},";
    384   }
    385 }
    386 
    387 void ModUnionTableReferenceCache::VisitObjects(ObjectCallback callback, void* arg) {
    388   CardTable* const card_table = heap_->GetCardTable();
    389   ContinuousSpaceBitmap* live_bitmap = space_->GetLiveBitmap();
    390   for (uint8_t* card : cleared_cards_) {
    391     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
    392     uintptr_t end = start + CardTable::kCardSize;
    393     live_bitmap->VisitMarkedRange(start,
    394                                   end,
    395                                   [callback, arg](mirror::Object* obj) {
    396       callback(obj, arg);
    397     });
    398   }
    399   // This may visit the same card twice, TODO avoid this.
    400   for (const auto& pair : references_) {
    401     const uint8_t* card = pair.first;
    402     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
    403     uintptr_t end = start + CardTable::kCardSize;
    404     live_bitmap->VisitMarkedRange(start,
    405                                   end,
    406                                   [callback, arg](mirror::Object* obj) {
    407       callback(obj, arg);
    408     });
    409   }
    410 }
    411 
    412 void ModUnionTableReferenceCache::UpdateAndMarkReferences(MarkObjectVisitor* visitor) {
    413   CardTable* const card_table = heap_->GetCardTable();
    414   std::vector<mirror::HeapReference<mirror::Object>*> cards_references;
    415   // If has_target_reference is true then there was a GcRoot compressed reference which wasn't
    416   // added. In this case we need to keep the card dirty.
    417   // We don't know if the GcRoot addresses will remain constant, for example, classloaders have a
    418   // hash set of GcRoot which may be resized or modified.
    419   bool has_target_reference;
    420   ModUnionReferenceVisitor add_visitor(this, visitor, &cards_references, &has_target_reference);
    421   CardSet new_cleared_cards;
    422   for (uint8_t* card : cleared_cards_) {
    423     // Clear and re-compute alloc space references associated with this card.
    424     cards_references.clear();
    425     has_target_reference = false;
    426     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
    427     uintptr_t end = start + CardTable::kCardSize;
    428     space::ContinuousSpace* space =
    429         heap_->FindContinuousSpaceFromObject(reinterpret_cast<mirror::Object*>(start), false);
    430     DCHECK(space != nullptr);
    431     ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
    432     live_bitmap->VisitMarkedRange(start, end, add_visitor);
    433     // Update the corresponding references for the card.
    434     auto found = references_.find(card);
    435     if (found == references_.end()) {
    436       // Don't add card for an empty reference array.
    437       if (!cards_references.empty()) {
    438         references_.Put(card, cards_references);
    439       }
    440     } else {
    441       if (cards_references.empty()) {
    442         references_.erase(found);
    443       } else {
    444         found->second = cards_references;
    445       }
    446     }
    447     if (has_target_reference) {
    448       // Keep this card for next time since it contains a GcRoot which matches the
    449       // ShouldAddReference criteria. This usually occurs for class loaders.
    450       new_cleared_cards.insert(card);
    451     }
    452   }
    453   cleared_cards_ = std::move(new_cleared_cards);
    454   size_t count = 0;
    455   for (auto it = references_.begin(); it != references_.end();) {
    456     std::vector<mirror::HeapReference<mirror::Object>*>& references = it->second;
    457     // Since there is no card mark for setting a reference to null, we check each reference.
    458     // If all of the references of a card are null then we can remove that card. This is racy
    459     // with the mutators, but handled by rescanning dirty cards.
    460     bool all_null = true;
    461     for (mirror::HeapReference<mirror::Object>* obj_ptr : references) {
    462       if (obj_ptr->AsMirrorPtr() != nullptr) {
    463         all_null = false;
    464         visitor->MarkHeapReference(obj_ptr, /*do_atomic_update*/ false);
    465       }
    466     }
    467     count += references.size();
    468     if (!all_null) {
    469       ++it;
    470     } else {
    471       // All null references, erase the array from the set.
    472       it = references_.erase(it);
    473     }
    474   }
    475   if (VLOG_IS_ON(heap)) {
    476     VLOG(gc) << "Marked " << count << " references in mod union table";
    477   }
    478 }
    479 
    480 ModUnionTableCardCache::ModUnionTableCardCache(const std::string& name,
    481                                                Heap* heap,
    482                                                space::ContinuousSpace* space)
    483     : ModUnionTable(name, heap, space) {
    484   // Normally here we could use End() instead of Limit(), but for testing we may want to have a
    485   // mod-union table for a space which can still grow.
    486   if (!space->IsImageSpace()) {
    487     CHECK_ALIGNED(reinterpret_cast<uintptr_t>(space->Limit()), CardTable::kCardSize);
    488   }
    489   card_bitmap_.reset(CardBitmap::Create(
    490       "mod union bitmap", reinterpret_cast<uintptr_t>(space->Begin()),
    491       RoundUp(reinterpret_cast<uintptr_t>(space->Limit()), CardTable::kCardSize)));
    492 }
    493 
    494 class CardBitVisitor {
    495  public:
    496   CardBitVisitor(MarkObjectVisitor* visitor,
    497                  space::ContinuousSpace* space,
    498                  space::ContinuousSpace* immune_space,
    499                  ModUnionTable::CardBitmap* card_bitmap)
    500       : visitor_(visitor),
    501         space_(space),
    502         immune_space_(immune_space),
    503         bitmap_(space->GetLiveBitmap()),
    504         card_bitmap_(card_bitmap) {
    505     DCHECK(immune_space_ != nullptr);
    506   }
    507 
    508   void operator()(size_t bit_index) const {
    509     const uintptr_t start = card_bitmap_->AddrFromBitIndex(bit_index);
    510     DCHECK(space_->HasAddress(reinterpret_cast<mirror::Object*>(start)))
    511         << start << " " << *space_;
    512     bool reference_to_other_space = false;
    513     ModUnionScanImageRootVisitor scan_visitor(visitor_, space_, immune_space_,
    514                                               &reference_to_other_space);
    515     bitmap_->VisitMarkedRange(start, start + CardTable::kCardSize, scan_visitor);
    516     if (!reference_to_other_space) {
    517       // No non null reference to another space, clear the bit.
    518       card_bitmap_->ClearBit(bit_index);
    519     }
    520   }
    521 
    522  private:
    523   MarkObjectVisitor* const visitor_;
    524   space::ContinuousSpace* const space_;
    525   space::ContinuousSpace* const immune_space_;
    526   ContinuousSpaceBitmap* const bitmap_;
    527   ModUnionTable::CardBitmap* const card_bitmap_;
    528 };
    529 
    530 void ModUnionTableCardCache::ProcessCards() {
    531   CardTable* const card_table = GetHeap()->GetCardTable();
    532   ModUnionAddToCardBitmapVisitor visitor(card_bitmap_.get(), card_table);
    533   // Clear dirty cards in the this space and update the corresponding mod-union bits.
    534   card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor);
    535 }
    536 
    537 void ModUnionTableCardCache::ClearTable() {
    538   card_bitmap_->Bitmap::Clear();
    539 }
    540 
    541 // Mark all references to the alloc space(s).
    542 void ModUnionTableCardCache::UpdateAndMarkReferences(MarkObjectVisitor* visitor) {
    543   // TODO: Needs better support for multi-images? b/26317072
    544   space::ImageSpace* image_space =
    545       heap_->GetBootImageSpaces().empty() ? nullptr : heap_->GetBootImageSpaces()[0];
    546   // If we don't have an image space, just pass in space_ as the immune space. Pass in the same
    547   // space_ instead of image_space to avoid a null check in ModUnionUpdateObjectReferencesVisitor.
    548   CardBitVisitor bit_visitor(visitor, space_, image_space != nullptr ? image_space : space_,
    549       card_bitmap_.get());
    550   card_bitmap_->VisitSetBits(
    551       0, RoundUp(space_->Size(), CardTable::kCardSize) / CardTable::kCardSize, bit_visitor);
    552 }
    553 
    554 void ModUnionTableCardCache::VisitObjects(ObjectCallback callback, void* arg) {
    555   card_bitmap_->VisitSetBits(
    556       0,
    557       RoundUp(space_->Size(), CardTable::kCardSize) / CardTable::kCardSize,
    558       [this, callback, arg](size_t bit_index) {
    559         const uintptr_t start = card_bitmap_->AddrFromBitIndex(bit_index);
    560         DCHECK(space_->HasAddress(reinterpret_cast<mirror::Object*>(start)))
    561             << start << " " << *space_;
    562         space_->GetLiveBitmap()->VisitMarkedRange(start,
    563                                                   start + CardTable::kCardSize,
    564                                                   [callback, arg](mirror::Object* obj) {
    565           callback(obj, arg);
    566         });
    567       });
    568 }
    569 
    570 void ModUnionTableCardCache::Dump(std::ostream& os) {
    571   os << "ModUnionTable dirty cards: [";
    572   // TODO: Find cleaner way of doing this.
    573   for (uint8_t* addr = space_->Begin(); addr < AlignUp(space_->End(), CardTable::kCardSize);
    574       addr += CardTable::kCardSize) {
    575     if (card_bitmap_->Test(reinterpret_cast<uintptr_t>(addr))) {
    576       os << reinterpret_cast<void*>(addr) << "-"
    577          << reinterpret_cast<void*>(addr + CardTable::kCardSize) << "\n";
    578     }
    579   }
    580   os << "]";
    581 }
    582 
    583 void ModUnionTableCardCache::SetCards() {
    584   // Only clean up to the end since there cannot be any objects past the End() of the space.
    585   for (uint8_t* addr = space_->Begin(); addr < AlignUp(space_->End(), CardTable::kCardSize);
    586        addr += CardTable::kCardSize) {
    587     card_bitmap_->Set(reinterpret_cast<uintptr_t>(addr));
    588   }
    589 }
    590 
    591 bool ModUnionTableCardCache::ContainsCardFor(uintptr_t addr) {
    592   return card_bitmap_->Test(addr);
    593 }
    594 
    595 void ModUnionTableReferenceCache::SetCards() {
    596   for (uint8_t* addr = space_->Begin(); addr < AlignUp(space_->End(), CardTable::kCardSize);
    597        addr += CardTable::kCardSize) {
    598     cleared_cards_.insert(heap_->GetCardTable()->CardFromAddr(reinterpret_cast<void*>(addr)));
    599   }
    600 }
    601 
    602 bool ModUnionTableReferenceCache::ContainsCardFor(uintptr_t addr) {
    603   auto* card_ptr = heap_->GetCardTable()->CardFromAddr(reinterpret_cast<void*>(addr));
    604   return cleared_cards_.find(card_ptr) != cleared_cards_.end() ||
    605       references_.find(card_ptr) != references_.end();
    606 }
    607 
    608 }  // namespace accounting
    609 }  // namespace gc
    610 }  // namespace art
    611