| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/heap/scavenger.h" |
| |
| #include "src/heap/barrier.h" |
| #include "src/heap/heap-inl.h" |
| #include "src/heap/mark-compact-inl.h" |
| #include "src/heap/objects-visiting-inl.h" |
| #include "src/heap/scavenger-inl.h" |
| #include "src/heap/sweeper.h" |
| #include "src/objects-body-descriptors-inl.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| class IterateAndScavengePromotedObjectsVisitor final : public ObjectVisitor { |
| public: |
| IterateAndScavengePromotedObjectsVisitor(Heap* heap, Scavenger* scavenger, |
| bool record_slots) |
| : heap_(heap), scavenger_(scavenger), record_slots_(record_slots) {} |
| |
| inline void VisitPointers(HeapObject* host, Object** start, |
| Object** end) final { |
| for (Address slot_address = reinterpret_cast<Address>(start); |
| slot_address < reinterpret_cast<Address>(end); |
| slot_address += kPointerSize) { |
| Object** slot = reinterpret_cast<Object**>(slot_address); |
| Object* target = *slot; |
| scavenger_->PageMemoryFence(target); |
| |
| if (target->IsHeapObject()) { |
| if (heap_->InFromSpace(target)) { |
| scavenger_->ScavengeObject(reinterpret_cast<HeapObject**>(slot), |
| HeapObject::cast(target)); |
| target = *slot; |
| scavenger_->PageMemoryFence(target); |
| |
| if (heap_->InNewSpace(target)) { |
| SLOW_DCHECK(target->IsHeapObject()); |
| SLOW_DCHECK(heap_->InToSpace(target)); |
| RememberedSet<OLD_TO_NEW>::Insert(Page::FromAddress(slot_address), |
| slot_address); |
| } |
| SLOW_DCHECK(!MarkCompactCollector::IsOnEvacuationCandidate( |
| HeapObject::cast(target))); |
| } else if (record_slots_ && |
| MarkCompactCollector::IsOnEvacuationCandidate( |
| HeapObject::cast(target))) { |
| heap_->mark_compact_collector()->RecordSlot(host, slot, target); |
| } |
| } |
| } |
| } |
| |
| private: |
| Heap* const heap_; |
| Scavenger* const scavenger_; |
| const bool record_slots_; |
| }; |
| |
| Scavenger::Scavenger(Heap* heap, bool is_logging, CopiedList* copied_list, |
| PromotionList* promotion_list, int task_id) |
| : heap_(heap), |
| promotion_list_(promotion_list, task_id), |
| copied_list_(copied_list, task_id), |
| local_pretenuring_feedback_(kInitialLocalPretenuringFeedbackCapacity), |
| copied_size_(0), |
| promoted_size_(0), |
| allocator_(heap), |
| is_logging_(is_logging), |
| is_incremental_marking_(heap->incremental_marking()->IsMarking()), |
| is_compacting_(heap->incremental_marking()->IsCompacting()) {} |
| |
| void Scavenger::IterateAndScavengePromotedObject(HeapObject* target, int size) { |
| // We are not collecting slots on new space objects during mutation thus we |
| // have to scan for pointers to evacuation candidates when we promote |
| // objects. But we should not record any slots in non-black objects. Grey |
| // object's slots would be rescanned. White object might not survive until |
| // the end of collection it would be a violation of the invariant to record |
| // its slots. |
| const bool record_slots = |
| is_compacting_ && |
| heap()->incremental_marking()->atomic_marking_state()->IsBlack(target); |
| IterateAndScavengePromotedObjectsVisitor visitor(heap(), this, record_slots); |
| target->IterateBody(target->map()->instance_type(), size, &visitor); |
| } |
| |
| void Scavenger::AddPageToSweeperIfNecessary(MemoryChunk* page) { |
| AllocationSpace space = page->owner()->identity(); |
| if ((space == OLD_SPACE) && !page->SweepingDone()) { |
| heap()->mark_compact_collector()->sweeper()->AddPage( |
| space, reinterpret_cast<Page*>(page), |
| Sweeper::READD_TEMPORARY_REMOVED_PAGE); |
| } |
| } |
| |
| void Scavenger::ScavengePage(MemoryChunk* page) { |
| CodePageMemoryModificationScope memory_modification_scope(page); |
| RememberedSet<OLD_TO_NEW>::Iterate( |
| page, |
| [this](Address addr) { return CheckAndScavengeObject(heap_, addr); }, |
| SlotSet::KEEP_EMPTY_BUCKETS); |
| RememberedSet<OLD_TO_NEW>::IterateTyped( |
| page, [this](SlotType type, Address host_addr, Address addr) { |
| return UpdateTypedSlotHelper::UpdateTypedSlot( |
| heap_->isolate(), type, addr, [this](Object** addr) { |
| return CheckAndScavengeObject(heap(), |
| reinterpret_cast<Address>(addr)); |
| }); |
| }); |
| |
| AddPageToSweeperIfNecessary(page); |
| } |
| |
| void Scavenger::Process(OneshotBarrier* barrier) { |
| // Threshold when to switch processing the promotion list to avoid |
| // allocating too much backing store in the worklist. |
| const int kProcessPromotionListThreshold = kPromotionListSegmentSize / 2; |
| ScavengeVisitor scavenge_visitor(heap(), this); |
| |
| const bool have_barrier = barrier != nullptr; |
| bool done; |
| size_t objects = 0; |
| do { |
| done = true; |
| ObjectAndSize object_and_size; |
| while ((promotion_list_.LocalPushSegmentSize() < |
| kProcessPromotionListThreshold) && |
| copied_list_.Pop(&object_and_size)) { |
| scavenge_visitor.Visit(object_and_size.first); |
| done = false; |
| if (have_barrier && ((++objects % kInterruptThreshold) == 0)) { |
| if (!copied_list_.IsGlobalPoolEmpty()) { |
| barrier->NotifyAll(); |
| } |
| } |
| } |
| |
| while (promotion_list_.Pop(&object_and_size)) { |
| HeapObject* target = object_and_size.first; |
| int size = object_and_size.second; |
| DCHECK(!target->IsMap()); |
| IterateAndScavengePromotedObject(target, size); |
| done = false; |
| if (have_barrier && ((++objects % kInterruptThreshold) == 0)) { |
| if (!promotion_list_.IsGlobalPoolEmpty()) { |
| barrier->NotifyAll(); |
| } |
| } |
| } |
| } while (!done); |
| } |
| |
| void Scavenger::Finalize() { |
| heap()->MergeAllocationSitePretenuringFeedback(local_pretenuring_feedback_); |
| heap()->IncrementSemiSpaceCopiedObjectSize(copied_size_); |
| heap()->IncrementPromotedObjectsSize(promoted_size_); |
| allocator_.Finalize(); |
| } |
| |
| void RootScavengeVisitor::VisitRootPointer(Root root, Object** p) { |
| ScavengePointer(p); |
| } |
| |
| void RootScavengeVisitor::VisitRootPointers(Root root, Object** start, |
| Object** end) { |
| // Copy all HeapObject pointers in [start, end) |
| for (Object** p = start; p < end; p++) ScavengePointer(p); |
| } |
| |
| void RootScavengeVisitor::ScavengePointer(Object** p) { |
| Object* object = *p; |
| if (!heap_->InNewSpace(object)) return; |
| |
| scavenger_->ScavengeObject(reinterpret_cast<HeapObject**>(p), |
| reinterpret_cast<HeapObject*>(object)); |
| } |
| |
| } // namespace internal |
| } // namespace v8 |