summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Lokesh Gidra <lokeshgidra@google.com> 2018-11-09 17:10:47 -0800
committer Lokesh Gidra <lokeshgidra@google.com> 2018-12-05 01:08:43 -0800
commit519c1c7bb05af22cca3a21cfc78afce5bf9c300c (patch)
treeefc6f5bd69f708a345d1fed80ff9c379519383a4
parent94d4f5fa020561a1f828828dce1edf8fb5d4ab5c (diff)
Cleanup marking logic for non-moving objects
The marking logic for non-moving and large objects currently grays the read barrier state and sets the mark bitmap together. This causes the following complexities: 1) Setting the mark bitmap has to be done atomically as application threads also set the mark bitmap. 2) It requires using a separate false gray stack to handle cases where due to concurrent execution one thread succeeds in graying the object even after the object has been already marked. This can be simplified if non-moving/large objects are treated just like the unevac-space objects are. Marking the object involves testing whether the object is already marked in the bitmap or not. If not, then graying the read barrier state and pushing to the mark stack. Eventually, GC thread sets the mark bit non-atomically while processing the reference. Bug: 119273672 Bug: 112720851 Bug: 119629540 Test: art/test/testrunner/testrunner.py --64 Change-Id: I7050d545d59d5d8b1c90813a982e6f464b15d5e3
-rw-r--r--runtime/gc/collector/concurrent_copying-inl.h30
-rw-r--r--runtime/gc/collector/concurrent_copying.cc392
-rw-r--r--runtime/gc/collector/concurrent_copying.h8
3 files changed, 183 insertions, 247 deletions
diff --git a/runtime/gc/collector/concurrent_copying-inl.h b/runtime/gc/collector/concurrent_copying-inl.h
index 6b394c7061..3160422b32 100644
--- a/runtime/gc/collector/concurrent_copying-inl.h
+++ b/runtime/gc/collector/concurrent_copying-inl.h
@@ -242,15 +242,33 @@ inline bool ConcurrentCopying::IsMarkedInUnevacFromSpace(mirror::Object* from_re
// Use load-acquire on the read barrier pointer to ensure that we never see a black (non-gray)
// read barrier state with an unmarked bit due to reordering.
DCHECK(region_space_->IsInUnevacFromSpace(from_ref));
- if (kEnableGenerationalConcurrentCopyingCollection
- && young_gen_
- && !done_scanning_.load(std::memory_order_acquire)) {
- return from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState();
- }
if (kUseBakerReadBarrier && from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState()) {
return true;
+ } else if (!(kEnableGenerationalConcurrentCopyingCollection && young_gen_)
+ || done_scanning_.load(std::memory_order_acquire)) {
+ // If the card table scanning is not finished yet, then only read-barrier
+ // state should be checked. Checking the mark bitmap is unreliable as there
+ // may be some objects - whose corresponding card is dirty - which are
+ // marked in the mark bitmap, but cannot be considered marked unless their
+ // read-barrier state is set to Gray.
+ //
+ // Why read read-barrier state before checking done_scanning_?
+ // If the read-barrier state was read *after* done_scanning_, then there
+ // exists a concurrency race due to which even after the object is marked,
+ // read-barrier state is checked *after* that, this function will return
+ // false. The following scenario may cause the race:
+ //
+ // 1. Mutator thread reads done_scanning_ and upon finding it false, gets
+ // suspended before reading the object's read-barrier state.
+ // 2. GC thread finishes card-table scan and then sets done_scanning_ to
+ // true.
+ // 3. GC thread grays the object, scans it, marks in the bitmap, and then
+ // changes its read-barrier state back to non-gray.
+ // 4. Mutator thread resumes, reads the object's read-barrier state and
+ // returns false.
+ return region_space_bitmap_->Test(from_ref);
}
- return region_space_bitmap_->Test(from_ref);
+ return false;
}
} // namespace collector
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index fefe9ab475..d4275a0224 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -332,11 +332,6 @@ void ConcurrentCopying::InitializePhase() {
<< reinterpret_cast<void*>(region_space_->Limit());
}
CheckEmptyMarkStack();
- if (kIsDebugBuild) {
- MutexLock mu(Thread::Current(), mark_stack_lock_);
- CHECK(false_gray_stack_.empty());
- }
-
rb_mark_bit_stack_full_ = false;
mark_from_read_barrier_measurements_ = measure_read_barrier_slow_path_;
if (measure_read_barrier_slow_path_) {
@@ -1056,9 +1051,6 @@ void ConcurrentCopying::MarkingPhase() {
Runtime::Current()->GetClassLinker()->CleanupClassLoaders();
// Marking is done. Disable marking.
DisableMarking();
- if (kUseBakerReadBarrier) {
- ProcessFalseGrayStack();
- }
CheckEmptyMarkStack();
}
@@ -1170,32 +1162,6 @@ void ConcurrentCopying::DisableMarking() {
mark_stack_mode_.store(kMarkStackModeOff, std::memory_order_seq_cst);
}
-void ConcurrentCopying::PushOntoFalseGrayStack(Thread* const self, mirror::Object* ref) {
- CHECK(kUseBakerReadBarrier);
- DCHECK(ref != nullptr);
- MutexLock mu(self, mark_stack_lock_);
- false_gray_stack_.push_back(ref);
-}
-
-void ConcurrentCopying::ProcessFalseGrayStack() {
- CHECK(kUseBakerReadBarrier);
- // Change the objects on the false gray stack from gray to non-gray (conceptually black).
- MutexLock mu(Thread::Current(), mark_stack_lock_);
- for (mirror::Object* obj : false_gray_stack_) {
- DCHECK(IsMarked(obj));
- // The object could be non-gray (conceptually black) here if a thread got preempted after a
- // success at the AtomicSetReadBarrierState in MarkNonMoving(), GC started marking through it
- // (but not finished so still gray), the thread ran to register it onto the false gray stack,
- // and then GC eventually marked it black (non-gray) after it finished scanning it.
- if (obj->GetReadBarrierState() == ReadBarrier::GrayState()) {
- bool success = obj->AtomicSetReadBarrierState(ReadBarrier::GrayState(),
- ReadBarrier::NonGrayState());
- DCHECK(success);
- }
- }
- false_gray_stack_.clear();
-}
-
void ConcurrentCopying::IssueEmptyCheckpoint() {
Thread* self = Thread::Current();
ThreadList* thread_list = Runtime::Current()->GetThreadList();
@@ -1418,24 +1384,6 @@ void ConcurrentCopying::VerifyNoFromSpaceReferences() {
}
// The following visitors are used to assert the to-space invariant.
-class ConcurrentCopying::AssertToSpaceInvariantRefsVisitor {
- public:
- explicit AssertToSpaceInvariantRefsVisitor(ConcurrentCopying* collector)
- : collector_(collector) {}
-
- void operator()(mirror::Object* ref) const
- REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
- if (ref == nullptr) {
- // OK.
- return;
- }
- collector_->AssertToSpaceInvariant(nullptr, MemberOffset(0), ref);
- }
-
- private:
- ConcurrentCopying* const collector_;
-};
-
class ConcurrentCopying::AssertToSpaceInvariantFieldVisitor {
public:
explicit AssertToSpaceInvariantFieldVisitor(ConcurrentCopying* collector)
@@ -1447,8 +1395,7 @@ class ConcurrentCopying::AssertToSpaceInvariantFieldVisitor {
REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
mirror::Object* ref =
obj->GetFieldObject<mirror::Object, kDefaultVerifyFlags, kWithoutReadBarrier>(offset);
- AssertToSpaceInvariantRefsVisitor visitor(collector_);
- visitor(ref);
+ collector_->AssertToSpaceInvariant(obj.Ptr(), offset, ref);
}
void operator()(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Reference> ref ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
@@ -1464,8 +1411,8 @@ class ConcurrentCopying::AssertToSpaceInvariantFieldVisitor {
void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
REQUIRES_SHARED(Locks::mutator_lock_) {
- AssertToSpaceInvariantRefsVisitor visitor(collector_);
- visitor(root->AsMirrorPtr());
+ mirror::Object* ref = root->AsMirrorPtr();
+ collector_->AssertToSpaceInvariant(/* obj */ nullptr, MemberOffset(0), ref);
}
private:
@@ -1671,30 +1618,69 @@ inline void ConcurrentCopying::ProcessMarkStackRef(mirror::Object* to_ref) {
// Invariant: There should be no object from a newly-allocated
// region (either large or non-large) on the mark stack.
DCHECK(!region_space_->IsInNewlyAllocatedRegion(to_ref)) << to_ref;
- if (rtype == space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace) {
- // Mark the bitmap only in the GC thread here so that we don't need a CAS.
- if (!kUseBakerReadBarrier ||
- !region_space_bitmap_->Set(to_ref)) {
- // It may be already marked if we accidentally pushed the same object twice due to the racy
- // bitmap read in MarkUnevacFromSpaceRegion.
- if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
- CHECK(region_space_->IsLargeObject(to_ref));
- region_space_->ZeroLiveBytesForLargeObject(to_ref);
- Scan<true>(to_ref);
- } else {
- Scan<false>(to_ref);
+ bool perform_scan = false;
+ switch (rtype) {
+ case space::RegionSpace::RegionType::kRegionTypeUnevacFromSpace:
+ // Mark the bitmap only in the GC thread here so that we don't need a CAS.
+ if (!kUseBakerReadBarrier || !region_space_bitmap_->Set(to_ref)) {
+ // It may be already marked if we accidentally pushed the same object twice due to the racy
+ // bitmap read in MarkUnevacFromSpaceRegion.
+ if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
+ CHECK(region_space_->IsLargeObject(to_ref));
+ region_space_->ZeroLiveBytesForLargeObject(to_ref);
+ }
+ perform_scan = true;
+ // Only add to the live bytes if the object was not already marked and we are not the young
+ // GC.
+ add_to_live_bytes = true;
}
- // Only add to the live bytes if the object was not already marked and we are not the young
- // GC.
- add_to_live_bytes = true;
- }
- } else {
- if (kEnableGenerationalConcurrentCopyingCollection) {
- if (rtype == space::RegionSpace::RegionType::kRegionTypeToSpace) {
+ break;
+ case space::RegionSpace::RegionType::kRegionTypeToSpace:
+ if (kEnableGenerationalConcurrentCopyingCollection) {
// Copied to to-space, set the bit so that the next GC can scan objects.
region_space_bitmap_->Set(to_ref);
}
- }
+ perform_scan = true;
+ break;
+ default:
+ DCHECK(!region_space_->HasAddress(to_ref)) << to_ref;
+ DCHECK(!immune_spaces_.ContainsObject(to_ref));
+ // Non-moving or large-object space.
+ if (kUseBakerReadBarrier) {
+ accounting::ContinuousSpaceBitmap* mark_bitmap =
+ heap_->GetNonMovingSpace()->GetMarkBitmap();
+ const bool is_los = !mark_bitmap->HasAddress(to_ref);
+ if (is_los) {
+ if (!IsAligned<kPageSize>(to_ref)) {
+ // Ref is a large object that is not aligned, it must be heap
+ // corruption. Remove memory protection and dump data before
+ // AtomicSetReadBarrierState since it will fault if the address is not
+ // valid.
+ region_space_->Unprotect();
+ heap_->GetVerification()->LogHeapCorruption(/* obj */ nullptr,
+ MemberOffset(0),
+ to_ref,
+ /* fatal */ true);
+ }
+ DCHECK(heap_->GetLargeObjectsSpace())
+ << "ref=" << to_ref
+ << " doesn't belong to non-moving space and large object space doesn't exist";
+ accounting::LargeObjectBitmap* los_bitmap =
+ heap_->GetLargeObjectsSpace()->GetMarkBitmap();
+ DCHECK(los_bitmap->HasAddress(to_ref));
+ // Only the GC thread could be setting the LOS bit map hence doesn't
+ // need to be atomically done.
+ perform_scan = !los_bitmap->Set(to_ref);
+ } else {
+ // Only the GC thread could be setting the non-moving space bit map
+ // hence doesn't need to be atomically done.
+ perform_scan = !mark_bitmap->Set(to_ref);
+ }
+ } else {
+ perform_scan = true;
+ }
+ }
+ if (perform_scan) {
if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
Scan<true>(to_ref);
} else {
@@ -2145,7 +2131,10 @@ void ConcurrentCopying::AssertToSpaceInvariant(mirror::Object* obj,
mirror::Object* ref) {
CHECK_EQ(heap_->collector_type_, kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
if (is_asserting_to_space_invariant_) {
- if (region_space_->HasAddress(ref)) {
+ if (ref == nullptr) {
+ // OK.
+ return;
+ } else if (region_space_->HasAddress(ref)) {
// Check to-space invariant in region space (moving space).
using RegionType = space::RegionSpace::RegionType;
space::RegionSpace::RegionType type = region_space_->GetRegionTypeUnsafe(ref);
@@ -2248,7 +2237,10 @@ void ConcurrentCopying::AssertToSpaceInvariant(GcRootSource* gc_root_source,
mirror::Object* ref) {
CHECK_EQ(heap_->collector_type_, kCollectorTypeCC) << static_cast<size_t>(heap_->collector_type_);
if (is_asserting_to_space_invariant_) {
- if (region_space_->HasAddress(ref)) {
+ if (ref == nullptr) {
+ // OK.
+ return;
+ } else if (region_space_->HasAddress(ref)) {
// Check to-space invariant in region space (moving space).
using RegionType = space::RegionSpace::RegionType;
space::RegionSpace::RegionType type = region_space_->GetRegionTypeUnsafe(ref);
@@ -2326,14 +2318,17 @@ void ConcurrentCopying::LogFromSpaceRefHolder(mirror::Object* obj, MemberOffset
LOG(INFO) << "holder is in an immune image or the zygote space.";
} else {
LOG(INFO) << "holder is in a non-immune, non-moving (or main) space.";
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(obj);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(obj);
- CHECK(los_bitmap != nullptr) << "LOS bitmap covers the entire address range";
- bool is_los = mark_bitmap == nullptr;
+ accounting::ContinuousSpaceBitmap* mark_bitmap = heap_->GetNonMovingSpace()->GetMarkBitmap();
+ accounting::LargeObjectBitmap* los_bitmap = nullptr;
+ const bool is_los = !mark_bitmap->HasAddress(obj);
+ if (is_los) {
+ DCHECK(heap_->GetLargeObjectsSpace() && heap_->GetLargeObjectsSpace()->Contains(obj))
+ << "obj=" << obj
+ << " LOS bit map covers the entire lower 4GB address range";
+ los_bitmap = heap_->GetLargeObjectsSpace()->GetMarkBitmap();
+ }
if (!is_los && mark_bitmap->Test(obj)) {
- LOG(INFO) << "holder is marked in the mark bit map.";
+ LOG(INFO) << "holder is marked in the non-moving space mark bit map.";
} else if (is_los && los_bitmap->Test(obj)) {
LOG(INFO) << "holder is marked in the los bit map.";
} else {
@@ -2350,6 +2345,30 @@ void ConcurrentCopying::LogFromSpaceRefHolder(mirror::Object* obj, MemberOffset
LOG(INFO) << "offset=" << offset.SizeValue();
}
+bool ConcurrentCopying::IsMarkedInNonMovingSpace(mirror::Object* from_ref) {
+ DCHECK(!region_space_->HasAddress(from_ref)) << "ref=" << from_ref;
+ DCHECK(!immune_spaces_.ContainsObject(from_ref)) << "ref=" << from_ref;
+ if (kUseBakerReadBarrier && from_ref->GetReadBarrierStateAcquire() == ReadBarrier::GrayState()) {
+ return true;
+ } else if (!(kEnableGenerationalConcurrentCopyingCollection && young_gen_)
+ || done_scanning_.load(std::memory_order_acquire)) {
+ // Read the comment in IsMarkedInUnevacFromSpace()
+ accounting::ContinuousSpaceBitmap* mark_bitmap = heap_->GetNonMovingSpace()->GetMarkBitmap();
+ accounting::LargeObjectBitmap* los_bitmap = nullptr;
+ const bool is_los = !mark_bitmap->HasAddress(from_ref);
+ if (is_los) {
+ DCHECK(heap_->GetLargeObjectsSpace() && heap_->GetLargeObjectsSpace()->Contains(from_ref))
+ << "ref=" << from_ref
+ << " doesn't belong to non-moving space and large object space doesn't exist";
+ los_bitmap = heap_->GetLargeObjectsSpace()->GetMarkBitmap();
+ }
+ if (is_los ? los_bitmap->Test(from_ref) : mark_bitmap->Test(from_ref)) {
+ return true;
+ }
+ }
+ return IsOnAllocStack(from_ref);
+}
+
void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* obj,
mirror::Object* ref) {
CHECK(ref != nullptr);
@@ -2371,62 +2390,14 @@ void ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace(mirror::Object* o
}
} else {
// Non-moving space and large-object space (LOS) cases.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(ref);
- bool is_los = (mark_bitmap == nullptr);
-
- bool marked_in_non_moving_space_or_los =
- (kUseBakerReadBarrier
- && kEnableGenerationalConcurrentCopyingCollection
- && young_gen_
- && !done_scanning_.load(std::memory_order_acquire))
- // Don't use the mark bitmap to ensure `ref` is marked: check that the
- // read barrier state is gray instead. This is to take into account a
- // potential race between two read barriers on the same reference when the
- // young-generation collector is still scanning the dirty cards.
- //
- // For instance consider two concurrent read barriers on the same GC root
- // reference during the dirty-card-scanning step of a young-generation
- // collection. Both threads would call ReadBarrier::BarrierForRoot, which
- // would:
- // a. mark the reference (leading to a call to
- // ConcurrentCopying::MarkNonMoving); then
- // b. check the to-space invariant (leading to a call this
- // ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace -- this
- // method).
- //
- // In this situation, the following race could happen:
- // 1. Thread A successfully changes `ref`'s read barrier state from
- // non-gray (white) to gray (with AtomicSetReadBarrierState) in
- // ConcurrentCopying::MarkNonMoving, then gets preempted.
- // 2. Thread B also tries to change `ref`'s read barrier state with
- // AtomicSetReadBarrierState from non-gray to gray in
- // ConcurrentCopying::MarkNonMoving, but fails, as Thread A already
- // changed it.
- // 3. Because Thread B failed the previous CAS, it does *not* set the
- // bit in the mark bitmap for `ref`.
- // 4. Thread B checks the to-space invariant and calls
- // ConcurrentCopying::AssertToSpaceInvariantInNonMovingSpace: the bit
- // is not set in the mark bitmap for `ref`; checking that this bit is
- // set to check the to-space invariant is therefore not a reliable
- // test.
- // 5. (Note that eventually, Thread A will resume its execution and set
- // the bit for `ref` in the mark bitmap.)
- ? (ref->GetReadBarrierState() == ReadBarrier::GrayState())
- // It is safe to use the heap mark bitmap otherwise.
- : (!is_los && mark_bitmap->Test(ref)) || (is_los && los_bitmap->Test(ref));
-
// If `ref` is on the allocation stack, then it may not be
// marked live, but considered marked/alive (but not
// necessarily on the live stack).
- CHECK(marked_in_non_moving_space_or_los || IsOnAllocStack(ref))
+ CHECK(IsMarkedInNonMovingSpace(ref))
<< "Unmarked ref that's not on the allocation stack."
<< " obj=" << obj
<< " ref=" << ref
<< " rb_state=" << ref->GetReadBarrierState()
- << " is_los=" << std::boolalpha << is_los << std::noboolalpha
<< " is_marking=" << std::boolalpha << is_marking_ << std::noboolalpha
<< " young_gen=" << std::boolalpha << young_gen_ << std::noboolalpha
<< " done_scanning="
@@ -2780,12 +2751,6 @@ mirror::Object* ConcurrentCopying::Copy(Thread* const self,
LOG(FATAL) << "Object address=" << from_ref << " type=" << from_ref->PrettyTypeOf();
}
bytes_allocated = non_moving_space_bytes_allocated;
- // Mark it in the mark bitmap.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
- CHECK(mark_bitmap != nullptr);
- bool previously_marked_in_bitmap = mark_bitmap->AtomicTestAndSet(to_ref);
- CHECK(!previously_marked_in_bitmap);
}
}
DCHECK(to_ref != nullptr);
@@ -2832,10 +2797,6 @@ mirror::Object* ConcurrentCopying::Copy(Thread* const self,
DCHECK(heap_->non_moving_space_->HasAddress(to_ref));
DCHECK_EQ(bytes_allocated, non_moving_space_bytes_allocated);
// Free the non-moving-space chunk.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(to_ref);
- CHECK(mark_bitmap != nullptr);
- CHECK(mark_bitmap->Clear(to_ref));
heap_->non_moving_space_->Free(self, to_ref);
}
@@ -2884,6 +2845,14 @@ mirror::Object* ConcurrentCopying::Copy(Thread* const self,
} else {
DCHECK(heap_->non_moving_space_->HasAddress(to_ref));
DCHECK_EQ(bytes_allocated, non_moving_space_bytes_allocated);
+ if (!kEnableGenerationalConcurrentCopyingCollection || !young_gen_) {
+ // Mark it in the live bitmap.
+ CHECK(!heap_->non_moving_space_->GetLiveBitmap()->AtomicTestAndSet(to_ref));
+ }
+ if (!kUseBakerReadBarrier) {
+ // Mark it in the mark bitmap.
+ CHECK(!heap_->non_moving_space_->GetMarkBitmap()->AtomicTestAndSet(to_ref));
+ }
}
if (kUseBakerReadBarrier) {
DCHECK(to_ref->GetReadBarrierState() == ReadBarrier::GrayState());
@@ -2928,34 +2897,11 @@ mirror::Object* ConcurrentCopying::IsMarked(mirror::Object* from_ref) {
to_ref = from_ref;
} else {
// Non-immune non-moving space. Use the mark bitmap.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(from_ref);
- bool is_los = mark_bitmap == nullptr;
- if (!is_los && mark_bitmap->Test(from_ref)) {
+ if (IsMarkedInNonMovingSpace(from_ref)) {
// Already marked.
to_ref = from_ref;
} else {
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(from_ref);
- // We may not have a large object space for dex2oat, don't assume it exists.
- if (los_bitmap == nullptr) {
- CHECK(heap_->GetLargeObjectsSpace() == nullptr)
- << "LOS bitmap covers the entire address range " << from_ref
- << " " << heap_->DumpSpaces();
- }
- if (los_bitmap != nullptr && is_los && los_bitmap->Test(from_ref)) {
- // Already marked in LOS.
- to_ref = from_ref;
- } else {
- // Not marked.
- if (IsOnAllocStack(from_ref)) {
- // If on the allocation stack, it's considered marked.
- to_ref = from_ref;
- } else {
- // Not marked.
- to_ref = nullptr;
- }
- }
+ to_ref = nullptr;
}
}
}
@@ -2977,11 +2923,24 @@ mirror::Object* ConcurrentCopying::MarkNonMoving(Thread* const self,
DCHECK(!region_space_->HasAddress(ref)) << ref;
DCHECK(!immune_spaces_.ContainsObject(ref));
// Use the mark bitmap.
- accounting::ContinuousSpaceBitmap* mark_bitmap =
- heap_mark_bitmap_->GetContinuousSpaceBitmap(ref);
- accounting::LargeObjectBitmap* los_bitmap =
- heap_mark_bitmap_->GetLargeObjectBitmap(ref);
- bool is_los = mark_bitmap == nullptr;
+ accounting::ContinuousSpaceBitmap* mark_bitmap = heap_->GetNonMovingSpace()->GetMarkBitmap();
+ accounting::LargeObjectBitmap* los_bitmap = nullptr;
+ const bool is_los = !mark_bitmap->HasAddress(ref);
+ if (is_los) {
+ if (!IsAligned<kPageSize>(ref)) {
+ // Ref is a large object that is not aligned, it must be heap
+ // corruption. Remove memory protection and dump data before
+ // AtomicSetReadBarrierState since it will fault if the address is not
+ // valid.
+ region_space_->Unprotect();
+ heap_->GetVerification()->LogHeapCorruption(holder, offset, ref, /* fatal= */ true);
+ }
+ DCHECK(heap_->GetLargeObjectsSpace())
+ << "ref=" << ref
+ << " doesn't belong to non-moving space and large object space doesn't exist";
+ los_bitmap = heap_->GetLargeObjectsSpace()->GetMarkBitmap();
+ DCHECK(los_bitmap->HasAddress(ref));
+ }
if (kEnableGenerationalConcurrentCopyingCollection && young_gen_) {
// The sticky-bit CC collector is only compatible with Baker-style read barriers.
DCHECK(kUseBakerReadBarrier);
@@ -2999,12 +2958,6 @@ mirror::Object* ConcurrentCopying::MarkNonMoving(Thread* const self,
ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(), ReadBarrier::GrayState())) {
// TODO: We don't actually need to scan this object later, we just need to clear the gray
// bit.
- // Also make sure the object is marked.
- if (is_los) {
- los_bitmap->AtomicTestAndSet(ref);
- } else {
- mark_bitmap->AtomicTestAndSet(ref);
- }
// We don't need to mark newly allocated objects (those in allocation stack) as they can
// only point to to-space objects. Also, they are considered live till the next GC cycle.
PushOntoMarkStack(self, ref);
@@ -3016,65 +2969,34 @@ mirror::Object* ConcurrentCopying::MarkNonMoving(Thread* const self,
// Already marked.
} else if (is_los && los_bitmap->Test(ref)) {
// Already marked in LOS.
+ } else if (IsOnAllocStack(ref)) {
+ // If it's on the allocation stack, it's considered marked. Keep it white (non-gray).
+ // Objects on the allocation stack need not be marked.
+ if (!is_los) {
+ DCHECK(!mark_bitmap->Test(ref));
+ } else {
+ DCHECK(!los_bitmap->Test(ref));
+ }
+ if (kUseBakerReadBarrier) {
+ DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::NonGrayState());
+ }
} else {
- // Not marked.
- if (IsOnAllocStack(ref)) {
- // If it's on the allocation stack, it's considered marked. Keep it white (non-gray).
- // Objects on the allocation stack need not be marked.
- if (!is_los) {
- DCHECK(!mark_bitmap->Test(ref));
- } else {
- DCHECK(!los_bitmap->Test(ref));
- }
- if (kUseBakerReadBarrier) {
- DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::NonGrayState());
- }
+ // Not marked nor on the allocation stack. Try to mark it.
+ // This may or may not succeed, which is ok.
+ bool success = false;
+ if (kUseBakerReadBarrier) {
+ success = ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(),
+ ReadBarrier::GrayState());
} else {
- // For the baker-style RB, we need to handle 'false-gray' cases. See the
- // kRegionTypeUnevacFromSpace-case comment in Mark().
- if (kUseBakerReadBarrier) {
- // Test the bitmap first to reduce the chance of false gray cases.
- if ((!is_los && mark_bitmap->Test(ref)) ||
- (is_los && los_bitmap->Test(ref))) {
- return ref;
- }
- }
- if (is_los && !IsAligned<kPageSize>(ref)) {
- // Ref is a large object that is not aligned, it must be heap
- // corruption. Remove memory protection and dump data before
- // AtomicSetReadBarrierState since it will fault if the address is not
- // valid.
- region_space_->Unprotect();
- heap_->GetVerification()->LogHeapCorruption(holder, offset, ref, /* fatal= */ true);
- }
- // Not marked nor on the allocation stack. Try to mark it.
- // This may or may not succeed, which is ok.
- bool cas_success = false;
+ success = is_los ?
+ !los_bitmap->AtomicTestAndSet(ref) :
+ !mark_bitmap->AtomicTestAndSet(ref);
+ }
+ if (success) {
if (kUseBakerReadBarrier) {
- cas_success = ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(),
- ReadBarrier::GrayState());
- }
- if (!is_los && mark_bitmap->AtomicTestAndSet(ref)) {
- // Already marked.
- if (kUseBakerReadBarrier &&
- cas_success &&
- ref->GetReadBarrierState() == ReadBarrier::GrayState()) {
- PushOntoFalseGrayStack(self, ref);
- }
- } else if (is_los && los_bitmap->AtomicTestAndSet(ref)) {
- // Already marked in LOS.
- if (kUseBakerReadBarrier &&
- cas_success &&
- ref->GetReadBarrierState() == ReadBarrier::GrayState()) {
- PushOntoFalseGrayStack(self, ref);
- }
- } else {
- // Newly marked.
- if (kUseBakerReadBarrier) {
- DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
- }
- PushOntoMarkStack(self, ref);
+ DCHECK_EQ(ref->GetReadBarrierState(), ReadBarrier::GrayState());
}
+ PushOntoMarkStack(self, ref);
}
}
return ref;
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index 6535b11d79..237e070d1a 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -218,6 +218,8 @@ class ConcurrentCopying : public GarbageCollector {
REQUIRES(!mark_stack_lock_, !skipped_blocks_lock_, !immune_gray_stack_lock_);
bool IsMarkedInUnevacFromSpace(mirror::Object* from_ref)
REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsMarkedInNonMovingSpace(mirror::Object* from_ref)
+ REQUIRES_SHARED(Locks::mutator_lock_);
bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* field,
bool do_atomic_update) override
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -283,11 +285,6 @@ class ConcurrentCopying : public GarbageCollector {
ALWAYS_INLINE mirror::Object* MarkImmuneSpace(Thread* const self,
mirror::Object* from_ref)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!immune_gray_stack_lock_);
- void PushOntoFalseGrayStack(Thread* const self, mirror::Object* obj)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!mark_stack_lock_);
- void ProcessFalseGrayStack() REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!mark_stack_lock_);
void ScanImmuneObject(mirror::Object* obj)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!mark_stack_lock_);
mirror::Object* MarkFromReadBarrierWithMeasurements(Thread* const self,
@@ -315,7 +312,6 @@ class ConcurrentCopying : public GarbageCollector {
// (see use case in ConcurrentCopying::MarkFromReadBarrier).
bool rb_mark_bit_stack_full_;
- std::vector<mirror::Object*> false_gray_stack_ GUARDED_BY(mark_stack_lock_);
Mutex mark_stack_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::vector<accounting::ObjectStack*> revoked_mark_stacks_
GUARDED_BY(mark_stack_lock_);