Fix reference-type obj handling in generational CC
Reference-type objects in old generation with referent in young
generation are not handled correctly. During card-table scan, we only
visit such objects, which does not process the referent. Also, we don't
set the read-barrier state to gray. Eventually, if the mutator calls
GetReferent() on such objects (after done_scanning_ is set to true),
then the read barrier will not be called, breaking the to-space
invariant.
Test: art/tests/testrunner/testrunner.py --target
Bug: 120792243
Change-Id: I53e8f0bc99ef99eb6e0ea7d743a6185d37d7ff8c
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 7736568..53aa9ba 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -860,6 +860,21 @@
ConcurrentCopying* const collector_;
};
+template <bool kNoUnEvac>
+void ConcurrentCopying::ScanDirtyObject(mirror::Object* obj) {
+ Scan<kNoUnEvac>(obj);
+ // Set the read-barrier state of a reference-type object to gray if its
+ // referent is not marked yet. This is to ensure that if GetReferent() is
+ // called, it triggers the read-barrier to process the referent before use.
+ if (UNLIKELY((obj->GetClass<kVerifyNone, kWithoutReadBarrier>()->IsTypeOfReferenceClass()))) {
+ mirror::Object* referent =
+ obj->AsReference<kVerifyNone, kWithoutReadBarrier>()->GetReferent<kWithoutReadBarrier>();
+ if (referent != nullptr && !IsInToSpace(referent)) {
+ obj->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(), ReadBarrier::GrayState());
+ }
+ }
+}
+
// Concurrently mark roots that are guarded by read barriers and process the mark stack.
void ConcurrentCopying::MarkingPhase() {
TimingLogger::ScopedTiming split("MarkingPhase", GetTimings());
@@ -924,7 +939,7 @@
LOG(FATAL) << "Scanning " << obj << " not in unevac space";
}
}
- Scan<true>(obj);
+ ScanDirtyObject</*kNoUnEvac*/ true>(obj);
},
accounting::CardTable::kCardDirty - 1);
}