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