612 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			612 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright (C) 2012 The Android Open Source Project
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  *      http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| #include "mod_union_table.h"
 | |
| 
 | |
| #include <memory>
 | |
| 
 | |
| #include "base/logging.h"  // For VLOG
 | |
| #include "base/stl_util.h"
 | |
| #include "bitmap-inl.h"
 | |
| #include "card_table-inl.h"
 | |
| #include "gc/accounting/space_bitmap-inl.h"
 | |
| #include "gc/heap.h"
 | |
| #include "gc/space/image_space.h"
 | |
| #include "gc/space/space.h"
 | |
| #include "mirror/object-inl.h"
 | |
| #include "mirror/object-refvisitor-inl.h"
 | |
| #include "object_callbacks.h"
 | |
| #include "space_bitmap-inl.h"
 | |
| #include "thread-current-inl.h"
 | |
| 
 | |
| namespace art {
 | |
| namespace gc {
 | |
| namespace accounting {
 | |
| 
 | |
| class ModUnionAddToCardSetVisitor {
 | |
|  public:
 | |
|   explicit ModUnionAddToCardSetVisitor(ModUnionTable::CardSet* const cleared_cards)
 | |
|       : cleared_cards_(cleared_cards) {}
 | |
| 
 | |
|   inline void operator()(uint8_t* card,
 | |
|                          uint8_t expected_value,
 | |
|                          uint8_t new_value ATTRIBUTE_UNUSED) const {
 | |
|     if (expected_value == CardTable::kCardDirty) {
 | |
|       cleared_cards_->insert(card);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ModUnionTable::CardSet* const cleared_cards_;
 | |
| };
 | |
| 
 | |
| class ModUnionAddToCardBitmapVisitor {
 | |
|  public:
 | |
|   ModUnionAddToCardBitmapVisitor(ModUnionTable::CardBitmap* bitmap, CardTable* card_table)
 | |
|       : bitmap_(bitmap), card_table_(card_table) {}
 | |
| 
 | |
|   inline void operator()(uint8_t* card,
 | |
|                          uint8_t expected_value,
 | |
|                          uint8_t new_value ATTRIBUTE_UNUSED) const {
 | |
|     if (expected_value == CardTable::kCardDirty) {
 | |
|       // We want the address the card represents, not the address of the card.
 | |
|       bitmap_->Set(reinterpret_cast<uintptr_t>(card_table_->AddrFromCard(card)));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ModUnionTable::CardBitmap* const bitmap_;
 | |
|   CardTable* const card_table_;
 | |
| };
 | |
| 
 | |
| class ModUnionAddToCardVectorVisitor {
 | |
|  public:
 | |
|   explicit ModUnionAddToCardVectorVisitor(std::vector<uint8_t*>* cleared_cards)
 | |
|       : cleared_cards_(cleared_cards) {
 | |
|   }
 | |
| 
 | |
|   void operator()(uint8_t* card, uint8_t expected_card, uint8_t new_card ATTRIBUTE_UNUSED) const {
 | |
|     if (expected_card == CardTable::kCardDirty) {
 | |
|       cleared_cards_->push_back(card);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   std::vector<uint8_t*>* const cleared_cards_;
 | |
| };
 | |
| 
 | |
| class ModUnionUpdateObjectReferencesVisitor {
 | |
|  public:
 | |
|   ModUnionUpdateObjectReferencesVisitor(MarkObjectVisitor* visitor,
 | |
|                                         space::ContinuousSpace* from_space,
 | |
|                                         space::ContinuousSpace* immune_space,
 | |
|                                         bool* contains_reference_to_other_space)
 | |
|     : visitor_(visitor),
 | |
|       from_space_(from_space),
 | |
|       immune_space_(immune_space),
 | |
|       contains_reference_to_other_space_(contains_reference_to_other_space) {}
 | |
| 
 | |
|   // Extra parameters are required since we use this same visitor signature for checking objects.
 | |
|   void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
 | |
|       REQUIRES_SHARED(Locks::mutator_lock_) {
 | |
|     MarkReference(obj->GetFieldObjectReferenceAddr(offset));
 | |
|   }
 | |
| 
 | |
|   void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
 | |
|       REQUIRES_SHARED(Locks::mutator_lock_) {
 | |
|     VisitRoot(root);
 | |
|   }
 | |
| 
 | |
|   void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
 | |
|       REQUIRES_SHARED(Locks::mutator_lock_) {
 | |
|     MarkReference(root);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   template<typename CompressedReferenceType>
 | |
|   void MarkReference(CompressedReferenceType* obj_ptr) const
 | |
|       REQUIRES_SHARED(Locks::mutator_lock_) {
 | |
|     // Only add the reference if it is non null and fits our criteria.
 | |
|     mirror::Object* ref = obj_ptr->AsMirrorPtr();
 | |
|     if (ref != nullptr && !from_space_->HasAddress(ref) && !immune_space_->HasAddress(ref)) {
 | |
|       *contains_reference_to_other_space_ = true;
 | |
|       mirror::Object* new_object = visitor_->MarkObject(ref);
 | |
|       if (ref != new_object) {
 | |
|         obj_ptr->Assign(new_object);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   MarkObjectVisitor* const visitor_;
 | |
|   // Space which we are scanning
 | |
|   space::ContinuousSpace* const from_space_;
 | |
|   space::ContinuousSpace* const immune_space_;
 | |
|   // Set if we have any references to another space.
 | |
|   bool* const contains_reference_to_other_space_;
 | |
| };
 | |
| 
 | |
| class ModUnionScanImageRootVisitor {
 | |
|  public:
 | |
|   // Immune space is any other space which we don't care about references to. Currently this is
 | |
|   // the image space in the case of the zygote mod union table.
 | |
|   ModUnionScanImageRootVisitor(MarkObjectVisitor* visitor,
 | |
|                                space::ContinuousSpace* from_space,
 | |
|                                space::ContinuousSpace* immune_space,
 | |
|                                bool* contains_reference_to_other_space)
 | |
|       : visitor_(visitor),
 | |
|         from_space_(from_space),
 | |
|         immune_space_(immune_space),
 | |
|         contains_reference_to_other_space_(contains_reference_to_other_space) {}
 | |
| 
 | |
|   void operator()(mirror::Object* root) const
 | |
|       REQUIRES(Locks::heap_bitmap_lock_)
 | |
|       REQUIRES_SHARED(Locks::mutator_lock_) {
 | |
|     DCHECK(root != nullptr);
 | |
|     ModUnionUpdateObjectReferencesVisitor ref_visitor(visitor_,
 | |
|                                                       from_space_,
 | |
|                                                       immune_space_,
 | |
|                                                       contains_reference_to_other_space_);
 | |
|     root->VisitReferences(ref_visitor, VoidFunctor());
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   MarkObjectVisitor* const visitor_;
 | |
|   // Space which we are scanning
 | |
|   space::ContinuousSpace* const from_space_;
 | |
|   space::ContinuousSpace* const immune_space_;
 | |
|   // Set if we have any references to another space.
 | |
|   bool* const contains_reference_to_other_space_;
 | |
| };
 | |
| 
 | |
| void ModUnionTableReferenceCache::ProcessCards() {
 | |
|   CardTable* card_table = GetHeap()->GetCardTable();
 | |
|   ModUnionAddToCardSetVisitor visitor(&cleared_cards_);
 | |
|   // Clear dirty cards in the this space and update the corresponding mod-union bits.
 | |
|   card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor);
 | |
| }
 | |
| 
 | |
| void ModUnionTableReferenceCache::ClearTable() {
 | |
|   cleared_cards_.clear();
 | |
|   references_.clear();
 | |
| }
 | |
| 
 | |
| class AddToReferenceArrayVisitor {
 | |
|  public:
 | |
|   AddToReferenceArrayVisitor(ModUnionTableReferenceCache* mod_union_table,
 | |
|                              MarkObjectVisitor* visitor,
 | |
|                              std::vector<mirror::HeapReference<mirror::Object>*>* references,
 | |
|                              bool* has_target_reference)
 | |
|       : mod_union_table_(mod_union_table),
 | |
|         visitor_(visitor),
 | |
|         references_(references),
 | |
|         has_target_reference_(has_target_reference) {}
 | |
| 
 | |
|   // Extra parameters are required since we use this same visitor signature for checking objects.
 | |
|   void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
 | |
|       REQUIRES_SHARED(Locks::mutator_lock_) {
 | |
|     mirror::HeapReference<mirror::Object>* ref_ptr = obj->GetFieldObjectReferenceAddr(offset);
 | |
|     mirror::Object* ref = ref_ptr->AsMirrorPtr();
 | |
|     // Only add the reference if it is non null and fits our criteria.
 | |
|     if (ref != nullptr && mod_union_table_->ShouldAddReference(ref)) {
 | |
|       // Push the adddress of the reference.
 | |
|       references_->push_back(ref_ptr);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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 (mod_union_table_->ShouldAddReference(root->AsMirrorPtr())) {
 | |
|       *has_target_reference_ = true;
 | |
|       // TODO: Add MarkCompressedReference callback here.
 | |
|       mirror::Object* old_ref = root->AsMirrorPtr();
 | |
|       mirror::Object* new_ref = visitor_->MarkObject(old_ref);
 | |
|       if (old_ref != new_ref) {
 | |
|         root->Assign(new_ref);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ModUnionTableReferenceCache* const mod_union_table_;
 | |
|   MarkObjectVisitor* const visitor_;
 | |
|   std::vector<mirror::HeapReference<mirror::Object>*>* const references_;
 | |
|   bool* const has_target_reference_;
 | |
| };
 | |
| 
 | |
| class ModUnionReferenceVisitor {
 | |
|  public:
 | |
|   ModUnionReferenceVisitor(ModUnionTableReferenceCache* const mod_union_table,
 | |
|                            MarkObjectVisitor* visitor,
 | |
|                            std::vector<mirror::HeapReference<mirror::Object>*>* references,
 | |
|                            bool* has_target_reference)
 | |
|       : mod_union_table_(mod_union_table),
 | |
|         visitor_(visitor),
 | |
|         references_(references),
 | |
|         has_target_reference_(has_target_reference) {}
 | |
| 
 | |
|   void operator()(mirror::Object* obj) const
 | |
|       REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
 | |
|     // We don't have an early exit since we use the visitor pattern, an early
 | |
|     // exit should significantly speed this up.
 | |
|     AddToReferenceArrayVisitor visitor(mod_union_table_,
 | |
|                                        visitor_,
 | |
|                                        references_,
 | |
|                                        has_target_reference_);
 | |
|     obj->VisitReferences(visitor, VoidFunctor());
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ModUnionTableReferenceCache* const mod_union_table_;
 | |
|   MarkObjectVisitor* const visitor_;
 | |
|   std::vector<mirror::HeapReference<mirror::Object>*>* const references_;
 | |
|   bool* const has_target_reference_;
 | |
| };
 | |
| 
 | |
| class CheckReferenceVisitor {
 | |
|  public:
 | |
|   CheckReferenceVisitor(ModUnionTableReferenceCache* mod_union_table,
 | |
|                         const std::set<mirror::Object*>& references)
 | |
|       : mod_union_table_(mod_union_table),
 | |
|         references_(references) {}
 | |
| 
 | |
|   // Extra parameters are required since we use this same visitor signature for checking objects.
 | |
|   void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
 | |
|       REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_) {
 | |
|     mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
 | |
|     if (ref != nullptr &&
 | |
|         mod_union_table_->ShouldAddReference(ref) &&
 | |
|         references_.find(ref) == references_.end()) {
 | |
|       Heap* heap = mod_union_table_->GetHeap();
 | |
|       space::ContinuousSpace* from_space = heap->FindContinuousSpaceFromObject(obj, false);
 | |
|       space::ContinuousSpace* to_space = heap->FindContinuousSpaceFromObject(ref, false);
 | |
|       LOG(INFO) << "Object " << reinterpret_cast<const void*>(obj) << "(" << obj->PrettyTypeOf()
 | |
|                 << ")" << "References "
 | |
|                 << reinterpret_cast<const void*>(ref) << "(" << mirror::Object::PrettyTypeOf(ref)
 | |
|           << ") without being in mod-union table";
 | |
|       LOG(INFO) << "FromSpace " << from_space->GetName() << " type "
 | |
|           << from_space->GetGcRetentionPolicy();
 | |
|       LOG(INFO) << "ToSpace " << to_space->GetName() << " type "
 | |
|           << to_space->GetGcRetentionPolicy();
 | |
|       heap->DumpSpaces(LOG_STREAM(INFO));
 | |
|       LOG(FATAL) << "FATAL ERROR";
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
 | |
|       REQUIRES_SHARED(Locks::mutator_lock_) {
 | |
|     if (kIsDebugBuild && !root->IsNull()) {
 | |
|       VisitRoot(root);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
 | |
|       REQUIRES_SHARED(Locks::mutator_lock_) {
 | |
|     DCHECK(!mod_union_table_->ShouldAddReference(root->AsMirrorPtr()));
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ModUnionTableReferenceCache* const mod_union_table_;
 | |
|   const std::set<mirror::Object*>& references_;
 | |
| };
 | |
| 
 | |
| class ModUnionCheckReferences {
 | |
|  public:
 | |
|   ModUnionCheckReferences(ModUnionTableReferenceCache* mod_union_table,
 | |
|                           const std::set<mirror::Object*>& references)
 | |
|       REQUIRES(Locks::heap_bitmap_lock_)
 | |
|       : mod_union_table_(mod_union_table), references_(references) {}
 | |
| 
 | |
|   void operator()(mirror::Object* obj) const NO_THREAD_SAFETY_ANALYSIS {
 | |
|     Locks::heap_bitmap_lock_->AssertSharedHeld(Thread::Current());
 | |
|     CheckReferenceVisitor visitor(mod_union_table_, references_);
 | |
|     obj->VisitReferences(visitor, VoidFunctor());
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   ModUnionTableReferenceCache* const mod_union_table_;
 | |
|   const std::set<mirror::Object*>& references_;
 | |
| };
 | |
| 
 | |
| class EmptyMarkObjectVisitor : public MarkObjectVisitor {
 | |
|  public:
 | |
|   mirror::Object* MarkObject(mirror::Object* obj) override {return obj;}
 | |
|   void MarkHeapReference(mirror::HeapReference<mirror::Object>*, bool) override {}
 | |
| };
 | |
| 
 | |
| void ModUnionTable::FilterCards() {
 | |
|   EmptyMarkObjectVisitor visitor;
 | |
|   // Use empty visitor since filtering is automatically done by UpdateAndMarkReferences.
 | |
|   UpdateAndMarkReferences(&visitor);
 | |
| }
 | |
| 
 | |
| void ModUnionTableReferenceCache::Verify() {
 | |
|   // Start by checking that everything in the mod union table is marked.
 | |
|   for (const auto& ref_pair : references_) {
 | |
|     for (mirror::HeapReference<mirror::Object>* ref : ref_pair.second) {
 | |
|       CHECK(heap_->IsLiveObjectLocked(ref->AsMirrorPtr()));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Check the references of each clean card which is also in the mod union table.
 | |
|   CardTable* card_table = heap_->GetCardTable();
 | |
|   ContinuousSpaceBitmap* live_bitmap = space_->GetLiveBitmap();
 | |
|   for (const auto& ref_pair : references_) {
 | |
|     const uint8_t* card = ref_pair.first;
 | |
|     if (*card == CardTable::kCardClean) {
 | |
|       std::set<mirror::Object*> reference_set;
 | |
|       for (mirror::HeapReference<mirror::Object>* obj_ptr : ref_pair.second) {
 | |
|         reference_set.insert(obj_ptr->AsMirrorPtr());
 | |
|       }
 | |
|       ModUnionCheckReferences visitor(this, reference_set);
 | |
|       uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
 | |
|       live_bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, visitor);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ModUnionTableReferenceCache::Dump(std::ostream& os) {
 | |
|   CardTable* card_table = heap_->GetCardTable();
 | |
|   os << "ModUnionTable cleared cards: [";
 | |
|   for (uint8_t* card_addr : cleared_cards_) {
 | |
|     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
 | |
|     uintptr_t end = start + CardTable::kCardSize;
 | |
|     os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << ",";
 | |
|   }
 | |
|   os << "]\nModUnionTable references: [";
 | |
|   for (const auto& ref_pair : references_) {
 | |
|     const uint8_t* card_addr = ref_pair.first;
 | |
|     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
 | |
|     uintptr_t end = start + CardTable::kCardSize;
 | |
|     os << reinterpret_cast<void*>(start) << "-" << reinterpret_cast<void*>(end) << "->{";
 | |
|     for (mirror::HeapReference<mirror::Object>* ref : ref_pair.second) {
 | |
|       os << reinterpret_cast<const void*>(ref->AsMirrorPtr()) << ",";
 | |
|     }
 | |
|     os << "},";
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ModUnionTableReferenceCache::VisitObjects(ObjectCallback callback, void* arg) {
 | |
|   CardTable* const card_table = heap_->GetCardTable();
 | |
|   ContinuousSpaceBitmap* live_bitmap = space_->GetLiveBitmap();
 | |
|   for (uint8_t* card : cleared_cards_) {
 | |
|     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
 | |
|     uintptr_t end = start + CardTable::kCardSize;
 | |
|     live_bitmap->VisitMarkedRange(start,
 | |
|                                   end,
 | |
|                                   [callback, arg](mirror::Object* obj) {
 | |
|       callback(obj, arg);
 | |
|     });
 | |
|   }
 | |
|   // This may visit the same card twice, TODO avoid this.
 | |
|   for (const auto& pair : references_) {
 | |
|     const uint8_t* card = pair.first;
 | |
|     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
 | |
|     uintptr_t end = start + CardTable::kCardSize;
 | |
|     live_bitmap->VisitMarkedRange(start,
 | |
|                                   end,
 | |
|                                   [callback, arg](mirror::Object* obj) {
 | |
|       callback(obj, arg);
 | |
|     });
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ModUnionTableReferenceCache::UpdateAndMarkReferences(MarkObjectVisitor* visitor) {
 | |
|   CardTable* const card_table = heap_->GetCardTable();
 | |
|   std::vector<mirror::HeapReference<mirror::Object>*> cards_references;
 | |
|   // If has_target_reference is true then there was a GcRoot compressed reference which wasn't
 | |
|   // added. In this case we need to keep the card dirty.
 | |
|   // We don't know if the GcRoot addresses will remain constant, for example, classloaders have a
 | |
|   // hash set of GcRoot which may be resized or modified.
 | |
|   bool has_target_reference;
 | |
|   ModUnionReferenceVisitor add_visitor(this, visitor, &cards_references, &has_target_reference);
 | |
|   CardSet new_cleared_cards;
 | |
|   for (uint8_t* card : cleared_cards_) {
 | |
|     // Clear and re-compute alloc space references associated with this card.
 | |
|     cards_references.clear();
 | |
|     has_target_reference = false;
 | |
|     uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
 | |
|     uintptr_t end = start + CardTable::kCardSize;
 | |
|     space::ContinuousSpace* space =
 | |
|         heap_->FindContinuousSpaceFromObject(reinterpret_cast<mirror::Object*>(start), false);
 | |
|     DCHECK(space != nullptr);
 | |
|     ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
 | |
|     live_bitmap->VisitMarkedRange(start, end, add_visitor);
 | |
|     // Update the corresponding references for the card.
 | |
|     auto found = references_.find(card);
 | |
|     if (found == references_.end()) {
 | |
|       // Don't add card for an empty reference array.
 | |
|       if (!cards_references.empty()) {
 | |
|         references_.Put(card, cards_references);
 | |
|       }
 | |
|     } else {
 | |
|       if (cards_references.empty()) {
 | |
|         references_.erase(found);
 | |
|       } else {
 | |
|         found->second = cards_references;
 | |
|       }
 | |
|     }
 | |
|     if (has_target_reference) {
 | |
|       // Keep this card for next time since it contains a GcRoot which matches the
 | |
|       // ShouldAddReference criteria. This usually occurs for class loaders.
 | |
|       new_cleared_cards.insert(card);
 | |
|     }
 | |
|   }
 | |
|   cleared_cards_ = std::move(new_cleared_cards);
 | |
|   size_t count = 0;
 | |
|   for (auto it = references_.begin(); it != references_.end();) {
 | |
|     std::vector<mirror::HeapReference<mirror::Object>*>& references = it->second;
 | |
|     // Since there is no card mark for setting a reference to null, we check each reference.
 | |
|     // If all of the references of a card are null then we can remove that card. This is racy
 | |
|     // with the mutators, but handled by rescanning dirty cards.
 | |
|     bool all_null = true;
 | |
|     for (mirror::HeapReference<mirror::Object>* obj_ptr : references) {
 | |
|       if (obj_ptr->AsMirrorPtr() != nullptr) {
 | |
|         all_null = false;
 | |
|         visitor->MarkHeapReference(obj_ptr, /*do_atomic_update=*/ false);
 | |
|       }
 | |
|     }
 | |
|     count += references.size();
 | |
|     if (!all_null) {
 | |
|       ++it;
 | |
|     } else {
 | |
|       // All null references, erase the array from the set.
 | |
|       it = references_.erase(it);
 | |
|     }
 | |
|   }
 | |
|   if (VLOG_IS_ON(heap)) {
 | |
|     VLOG(gc) << "Marked " << count << " references in mod union table";
 | |
|   }
 | |
| }
 | |
| 
 | |
| ModUnionTableCardCache::ModUnionTableCardCache(const std::string& name,
 | |
|                                                Heap* heap,
 | |
|                                                space::ContinuousSpace* space)
 | |
|     : ModUnionTable(name, heap, space) {
 | |
|   // Normally here we could use End() instead of Limit(), but for testing we may want to have a
 | |
|   // mod-union table for a space which can still grow.
 | |
|   if (!space->IsImageSpace()) {
 | |
|     CHECK_ALIGNED(reinterpret_cast<uintptr_t>(space->Limit()), CardTable::kCardSize);
 | |
|   }
 | |
|   card_bitmap_.reset(CardBitmap::Create(
 | |
|       "mod union bitmap", reinterpret_cast<uintptr_t>(space->Begin()),
 | |
|       RoundUp(reinterpret_cast<uintptr_t>(space->Limit()), CardTable::kCardSize)));
 | |
| }
 | |
| 
 | |
| class CardBitVisitor {
 | |
|  public:
 | |
|   CardBitVisitor(MarkObjectVisitor* visitor,
 | |
|                  space::ContinuousSpace* space,
 | |
|                  space::ContinuousSpace* immune_space,
 | |
|                  ModUnionTable::CardBitmap* card_bitmap)
 | |
|       : visitor_(visitor),
 | |
|         space_(space),
 | |
|         immune_space_(immune_space),
 | |
|         bitmap_(space->GetLiveBitmap()),
 | |
|         card_bitmap_(card_bitmap) {
 | |
|     DCHECK(immune_space_ != nullptr);
 | |
|   }
 | |
| 
 | |
|   void operator()(size_t bit_index) const {
 | |
|     const uintptr_t start = card_bitmap_->AddrFromBitIndex(bit_index);
 | |
|     DCHECK(space_->HasAddress(reinterpret_cast<mirror::Object*>(start)))
 | |
|         << start << " " << *space_;
 | |
|     bool reference_to_other_space = false;
 | |
|     ModUnionScanImageRootVisitor scan_visitor(visitor_, space_, immune_space_,
 | |
|                                               &reference_to_other_space);
 | |
|     bitmap_->VisitMarkedRange(start, start + CardTable::kCardSize, scan_visitor);
 | |
|     if (!reference_to_other_space) {
 | |
|       // No non null reference to another space, clear the bit.
 | |
|       card_bitmap_->ClearBit(bit_index);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   MarkObjectVisitor* const visitor_;
 | |
|   space::ContinuousSpace* const space_;
 | |
|   space::ContinuousSpace* const immune_space_;
 | |
|   ContinuousSpaceBitmap* const bitmap_;
 | |
|   ModUnionTable::CardBitmap* const card_bitmap_;
 | |
| };
 | |
| 
 | |
| void ModUnionTableCardCache::ProcessCards() {
 | |
|   CardTable* const card_table = GetHeap()->GetCardTable();
 | |
|   ModUnionAddToCardBitmapVisitor visitor(card_bitmap_.get(), card_table);
 | |
|   // Clear dirty cards in the this space and update the corresponding mod-union bits.
 | |
|   card_table->ModifyCardsAtomic(space_->Begin(), space_->End(), AgeCardVisitor(), visitor);
 | |
| }
 | |
| 
 | |
| void ModUnionTableCardCache::ClearTable() {
 | |
|   card_bitmap_->Bitmap::Clear();
 | |
| }
 | |
| 
 | |
| // Mark all references to the alloc space(s).
 | |
| void ModUnionTableCardCache::UpdateAndMarkReferences(MarkObjectVisitor* visitor) {
 | |
|   // TODO: Needs better support for multi-images? b/26317072
 | |
|   space::ImageSpace* image_space =
 | |
|       heap_->GetBootImageSpaces().empty() ? nullptr : heap_->GetBootImageSpaces()[0];
 | |
|   // If we don't have an image space, just pass in space_ as the immune space. Pass in the same
 | |
|   // space_ instead of image_space to avoid a null check in ModUnionUpdateObjectReferencesVisitor.
 | |
|   CardBitVisitor bit_visitor(visitor, space_, image_space != nullptr ? image_space : space_,
 | |
|       card_bitmap_.get());
 | |
|   card_bitmap_->VisitSetBits(
 | |
|       0, RoundUp(space_->Size(), CardTable::kCardSize) / CardTable::kCardSize, bit_visitor);
 | |
| }
 | |
| 
 | |
| void ModUnionTableCardCache::VisitObjects(ObjectCallback callback, void* arg) {
 | |
|   card_bitmap_->VisitSetBits(
 | |
|       0,
 | |
|       RoundUp(space_->Size(), CardTable::kCardSize) / CardTable::kCardSize,
 | |
|       [this, callback, arg](size_t bit_index) {
 | |
|         const uintptr_t start = card_bitmap_->AddrFromBitIndex(bit_index);
 | |
|         DCHECK(space_->HasAddress(reinterpret_cast<mirror::Object*>(start)))
 | |
|             << start << " " << *space_;
 | |
|         space_->GetLiveBitmap()->VisitMarkedRange(start,
 | |
|                                                   start + CardTable::kCardSize,
 | |
|                                                   [callback, arg](mirror::Object* obj) {
 | |
|           callback(obj, arg);
 | |
|         });
 | |
|       });
 | |
| }
 | |
| 
 | |
| void ModUnionTableCardCache::Dump(std::ostream& os) {
 | |
|   os << "ModUnionTable dirty cards: [";
 | |
|   // TODO: Find cleaner way of doing this.
 | |
|   for (uint8_t* addr = space_->Begin(); addr < AlignUp(space_->End(), CardTable::kCardSize);
 | |
|       addr += CardTable::kCardSize) {
 | |
|     if (card_bitmap_->Test(reinterpret_cast<uintptr_t>(addr))) {
 | |
|       os << reinterpret_cast<void*>(addr) << "-"
 | |
|          << reinterpret_cast<void*>(addr + CardTable::kCardSize) << "\n";
 | |
|     }
 | |
|   }
 | |
|   os << "]";
 | |
| }
 | |
| 
 | |
| void ModUnionTableCardCache::SetCards() {
 | |
|   // Only clean up to the end since there cannot be any objects past the End() of the space.
 | |
|   for (uint8_t* addr = space_->Begin(); addr < AlignUp(space_->End(), CardTable::kCardSize);
 | |
|        addr += CardTable::kCardSize) {
 | |
|     card_bitmap_->Set(reinterpret_cast<uintptr_t>(addr));
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool ModUnionTableCardCache::ContainsCardFor(uintptr_t addr) {
 | |
|   return card_bitmap_->Test(addr);
 | |
| }
 | |
| 
 | |
| void ModUnionTableReferenceCache::SetCards() {
 | |
|   for (uint8_t* addr = space_->Begin(); addr < AlignUp(space_->End(), CardTable::kCardSize);
 | |
|        addr += CardTable::kCardSize) {
 | |
|     cleared_cards_.insert(heap_->GetCardTable()->CardFromAddr(reinterpret_cast<void*>(addr)));
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool ModUnionTableReferenceCache::ContainsCardFor(uintptr_t addr) {
 | |
|   auto* card_ptr = heap_->GetCardTable()->CardFromAddr(reinterpret_cast<void*>(addr));
 | |
|   return cleared_cards_.find(card_ptr) != cleared_cards_.end() ||
 | |
|       references_.find(card_ptr) != references_.end();
 | |
| }
 | |
| 
 | |
| }  // namespace accounting
 | |
| }  // namespace gc
 | |
| }  // namespace art
 |