diff --git a/deps/v8/src/heap/heap.cc b/deps/v8/src/heap/heap.cc index b168c3104c7666..65526f748c4e6d 100644 --- a/deps/v8/src/heap/heap.cc +++ b/deps/v8/src/heap/heap.cc @@ -1814,7 +1814,11 @@ void Heap::Scavenge() { ArrayBufferTracker::FreeDeadInNewSpace(this); RememberedSet::IterateMemoryChunks(this, [](MemoryChunk* chunk) { - RememberedSet::PreFreeEmptyBuckets(chunk); + if (chunk->SweepingDone()) { + RememberedSet::FreeEmptyBuckets(chunk); + } else { + RememberedSet::PreFreeEmptyBuckets(chunk); + } }); // Update how much has survived scavenge. diff --git a/deps/v8/src/heap/remembered-set.h b/deps/v8/src/heap/remembered-set.h index 5908940d9e913c..543becfdf6ff76 100644 --- a/deps/v8/src/heap/remembered-set.h +++ b/deps/v8/src/heap/remembered-set.h @@ -150,6 +150,19 @@ class RememberedSet : public AllStatic { } } + static int NumberOfPreFreedEmptyBuckets(MemoryChunk* chunk) { + DCHECK(type == OLD_TO_NEW); + int result = 0; + SlotSet* slots = chunk->slot_set(); + if (slots != nullptr) { + size_t pages = (chunk->size() + Page::kPageSize - 1) / Page::kPageSize; + for (size_t page = 0; page < pages; page++) { + result += slots[page].NumberOfPreFreedEmptyBuckets(); + } + } + return result; + } + static void PreFreeEmptyBuckets(MemoryChunk* chunk) { DCHECK(type == OLD_TO_NEW); SlotSet* slots = chunk->slot_set(); @@ -161,6 +174,18 @@ class RememberedSet : public AllStatic { } } + static void FreeEmptyBuckets(MemoryChunk* chunk) { + DCHECK(type == OLD_TO_NEW); + SlotSet* slots = chunk->slot_set(); + if (slots != nullptr) { + size_t pages = (chunk->size() + Page::kPageSize - 1) / Page::kPageSize; + for (size_t page = 0; page < pages; page++) { + slots[page].FreeEmptyBuckets(); + slots[page].FreeToBeFreedBuckets(); + } + } + } + // Given a page and a typed slot in that page, this function adds the slot // to the remembered set. static void InsertTyped(Page* page, Address host_addr, SlotType slot_type, diff --git a/deps/v8/src/heap/slot-set.h b/deps/v8/src/heap/slot-set.h index 64ba266f21f46b..8da187d8a1057a 100644 --- a/deps/v8/src/heap/slot-set.h +++ b/deps/v8/src/heap/slot-set.h @@ -225,25 +225,33 @@ class SlotSet : public Malloced { return new_count; } + int NumberOfPreFreedEmptyBuckets() { + base::LockGuard guard(&to_be_freed_buckets_mutex_); + return static_cast(to_be_freed_buckets_.size()); + } + void PreFreeEmptyBuckets() { for (int bucket_index = 0; bucket_index < kBuckets; bucket_index++) { Bucket bucket = LoadBucket(&buckets_[bucket_index]); if (bucket != nullptr) { - bool found_non_empty_cell = false; - int cell_offset = bucket_index * kBitsPerBucket; - for (int i = 0; i < kCellsPerBucket; i++, cell_offset += kBitsPerCell) { - if (LoadCell(&bucket[i])) { - found_non_empty_cell = true; - break; - } - } - if (!found_non_empty_cell) { + if (IsEmptyBucket(bucket)) { PreFreeEmptyBucket(bucket_index); } } } } + void FreeEmptyBuckets() { + for (int bucket_index = 0; bucket_index < kBuckets; bucket_index++) { + Bucket bucket = LoadBucket(&buckets_[bucket_index]); + if (bucket != nullptr) { + if (IsEmptyBucket(bucket)) { + ReleaseBucket(bucket_index); + } + } + } + } + void FreeToBeFreedBuckets() { base::LockGuard guard(&to_be_freed_buckets_mutex_); while (!to_be_freed_buckets_.empty()) { @@ -251,6 +259,7 @@ class SlotSet : public Malloced { to_be_freed_buckets_.pop(); DeleteArray(top); } + DCHECK_EQ(0u, to_be_freed_buckets_.size()); } private: @@ -313,6 +322,15 @@ class SlotSet : public Malloced { } } + bool IsEmptyBucket(Bucket bucket) { + for (int i = 0; i < kCellsPerBucket; i++) { + if (LoadCell(&bucket[i])) { + return false; + } + } + return true; + } + template bool SwapInNewBucket(Bucket* bucket, Bucket value) { if (access_mode == AccessMode::ATOMIC) { diff --git a/deps/v8/src/heap/spaces.cc b/deps/v8/src/heap/spaces.cc index f275a4d518d9d4..798ef89ec1f201 100644 --- a/deps/v8/src/heap/spaces.cc +++ b/deps/v8/src/heap/spaces.cc @@ -3308,6 +3308,7 @@ void LargeObjectSpace::ClearMarkingStateOfLiveObjects() { Marking::MarkWhite( ObjectMarking::MarkBitFrom(obj, MarkingState::Internal(obj))); MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address()); + RememberedSet::FreeEmptyBuckets(chunk); chunk->ResetProgressBar(); MarkingState::Internal(chunk).SetLiveBytes(0); } diff --git a/deps/v8/test/cctest/heap/test-heap.cc b/deps/v8/test/cctest/heap/test-heap.cc index d9608292e8c0ed..a2273d2d8c9fe7 100644 --- a/deps/v8/test/cctest/heap/test-heap.cc +++ b/deps/v8/test/cctest/heap/test-heap.cc @@ -6237,6 +6237,53 @@ HEAP_TEST(Regress5831) { CHECK(chunk->NeverEvacuate()); } +TEST(Regress6800) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope handle_scope(isolate); + + const int kRootLength = 1000; + Handle root = + isolate->factory()->NewFixedArray(kRootLength, TENURED); + { + HandleScope inner_scope(isolate); + Handle new_space_array = isolate->factory()->NewFixedArray(1); + for (int i = 0; i < kRootLength; i++) { + root->set(i, *new_space_array); + } + for (int i = 0; i < kRootLength; i++) { + root->set(i, CcTest::heap()->undefined_value()); + } + } + CcTest::CollectGarbage(NEW_SPACE); + CHECK_EQ(0, RememberedSet::NumberOfPreFreedEmptyBuckets( + MemoryChunk::FromAddress(root->address()))); +} + +TEST(Regress6800LargeObject) { + CcTest::InitializeVM(); + Isolate* isolate = CcTest::i_isolate(); + HandleScope handle_scope(isolate); + + const int kRootLength = i::kMaxRegularHeapObjectSize / kPointerSize; + Handle root = + isolate->factory()->NewFixedArray(kRootLength, TENURED); + CcTest::heap()->lo_space()->Contains(*root); + { + HandleScope inner_scope(isolate); + Handle new_space_array = isolate->factory()->NewFixedArray(1); + for (int i = 0; i < kRootLength; i++) { + root->set(i, *new_space_array); + } + for (int i = 0; i < kRootLength; i++) { + root->set(i, CcTest::heap()->undefined_value()); + } + } + CcTest::CollectGarbage(OLD_SPACE); + CHECK_EQ(0, RememberedSet::NumberOfPreFreedEmptyBuckets( + MemoryChunk::FromAddress(root->address()))); +} + HEAP_TEST(RegressMissingWriteBarrierInAllocate) { if (!FLAG_incremental_marking) return; FLAG_black_allocation = true;