Index: src/heap/mark-compact.cc |
diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc |
index 3e5dafe345a481f08f88f20fd971db40e8894af7..1dccfda4654ac29612801699a753358a0012c620 100644 |
--- a/src/heap/mark-compact.cc |
+++ b/src/heap/mark-compact.cc |
@@ -19,14 +19,13 @@ |
#include "src/heap/incremental-marking.h" |
#include "src/heap/mark-compact-inl.h" |
#include "src/heap/object-stats.h" |
+#include "src/heap/objects-visiting.h" |
#include "src/heap/objects-visiting-inl.h" |
-#include "src/heap/objects-visiting.h" |
#include "src/heap/slots-buffer.h" |
#include "src/heap/spaces-inl.h" |
#include "src/ic/ic.h" |
#include "src/ic/stub-cache.h" |
#include "src/profiler/cpu-profiler.h" |
-#include "src/utils-inl.h" |
#include "src/v8.h" |
namespace v8 { |
@@ -321,7 +320,9 @@ |
{ |
GCTracer::Scope gc_scope(heap()->tracer(), |
GCTracer::Scope::MC_CLEAR_SLOTS_BUFFER); |
- for (Page* p : evacuation_candidates_) { |
+ int number_of_pages = evacuation_candidates_.length(); |
+ for (int i = 0; i < number_of_pages; i++) { |
+ Page* p = evacuation_candidates_[i]; |
SlotsBuffer::RemoveInvalidSlots(heap_, p->slots_buffer()); |
} |
} |
@@ -475,6 +476,30 @@ |
Page::FromAddress(obj->address())->ResetLiveBytes(); |
} |
} |
+ |
+ |
+class MarkCompactCollector::CompactionTask : public CancelableTask { |
+ public: |
+ explicit CompactionTask(Heap* heap, CompactionSpaceCollection* spaces) |
+ : CancelableTask(heap->isolate()), spaces_(spaces) {} |
+ |
+ virtual ~CompactionTask() {} |
+ |
+ private: |
+ // v8::internal::CancelableTask overrides. |
+ void RunInternal() override { |
+ MarkCompactCollector* mark_compact = |
+ isolate()->heap()->mark_compact_collector(); |
+ SlotsBuffer* evacuation_slots_buffer = nullptr; |
+ mark_compact->EvacuatePages(spaces_, &evacuation_slots_buffer); |
+ mark_compact->AddEvacuationSlotsBufferSynchronized(evacuation_slots_buffer); |
+ mark_compact->pending_compaction_tasks_semaphore_.Signal(); |
+ } |
+ |
+ CompactionSpaceCollection* spaces_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(CompactionTask); |
+}; |
class MarkCompactCollector::SweeperTask : public v8::Task { |
@@ -806,7 +831,9 @@ |
void MarkCompactCollector::AbortCompaction() { |
if (compacting_) { |
- for (Page* p : evacuation_candidates_) { |
+ int npages = evacuation_candidates_.length(); |
+ for (int i = 0; i < npages; i++) { |
+ Page* p = evacuation_candidates_[i]; |
slots_buffer_allocator_->DeallocateChain(p->slots_buffer_address()); |
p->ClearEvacuationCandidate(); |
p->ClearFlag(MemoryChunk::RESCAN_ON_EVACUATION); |
@@ -1521,13 +1548,8 @@ |
class MarkCompactCollector::EvacuateVisitorBase |
: public MarkCompactCollector::HeapObjectVisitor { |
public: |
- EvacuateVisitorBase(Heap* heap, CompactionSpaceCollection* compaction_spaces, |
- SlotsBuffer** evacuation_slots_buffer, |
- LocalStoreBuffer* local_store_buffer) |
- : heap_(heap), |
- evacuation_slots_buffer_(evacuation_slots_buffer), |
- compaction_spaces_(compaction_spaces), |
- local_store_buffer_(local_store_buffer) {} |
+ EvacuateVisitorBase(Heap* heap, SlotsBuffer** evacuation_slots_buffer) |
+ : heap_(heap), evacuation_slots_buffer_(evacuation_slots_buffer) {} |
bool TryEvacuateObject(PagedSpace* target_space, HeapObject* object, |
HeapObject** target_object) { |
@@ -1537,7 +1559,7 @@ |
if (allocation.To(target_object)) { |
heap_->mark_compact_collector()->MigrateObject( |
*target_object, object, size, target_space->identity(), |
- evacuation_slots_buffer_, local_store_buffer_); |
+ evacuation_slots_buffer_); |
return true; |
} |
return false; |
@@ -1546,8 +1568,6 @@ |
protected: |
Heap* heap_; |
SlotsBuffer** evacuation_slots_buffer_; |
- CompactionSpaceCollection* compaction_spaces_; |
- LocalStoreBuffer* local_store_buffer_; |
}; |
@@ -1558,12 +1578,9 @@ |
static const intptr_t kMaxLabObjectSize = 256; |
explicit EvacuateNewSpaceVisitor(Heap* heap, |
- CompactionSpaceCollection* compaction_spaces, |
SlotsBuffer** evacuation_slots_buffer, |
- LocalStoreBuffer* local_store_buffer, |
HashMap* local_pretenuring_feedback) |
- : EvacuateVisitorBase(heap, compaction_spaces, evacuation_slots_buffer, |
- local_store_buffer), |
+ : EvacuateVisitorBase(heap, evacuation_slots_buffer), |
buffer_(LocalAllocationBuffer::InvalidBuffer()), |
space_to_allocate_(NEW_SPACE), |
promoted_size_(0), |
@@ -1575,8 +1592,7 @@ |
int size = object->Size(); |
HeapObject* target_object = nullptr; |
if (heap_->ShouldBePromoted(object->address(), size) && |
- TryEvacuateObject(compaction_spaces_->Get(OLD_SPACE), object, |
- &target_object)) { |
+ TryEvacuateObject(heap_->old_space(), object, &target_object)) { |
// If we end up needing more special cases, we should factor this out. |
if (V8_UNLIKELY(target_object->IsJSArrayBuffer())) { |
heap_->array_buffer_tracker()->Promote( |
@@ -1589,8 +1605,7 @@ |
AllocationSpace space = AllocateTargetObject(object, &target); |
heap_->mark_compact_collector()->MigrateObject( |
HeapObject::cast(target), object, size, space, |
- (space == NEW_SPACE) ? nullptr : evacuation_slots_buffer_, |
- (space == NEW_SPACE) ? nullptr : local_store_buffer_); |
+ (space == NEW_SPACE) ? nullptr : evacuation_slots_buffer_); |
if (V8_UNLIKELY(target->IsJSArrayBuffer())) { |
heap_->array_buffer_tracker()->MarkLive(JSArrayBuffer::cast(target)); |
} |
@@ -1663,8 +1678,7 @@ |
inline AllocationResult AllocateInOldSpace(int size_in_bytes, |
AllocationAlignment alignment) { |
AllocationResult allocation = |
- compaction_spaces_->Get(OLD_SPACE)->AllocateRaw(size_in_bytes, |
- alignment); |
+ heap_->old_space()->AllocateRaw(size_in_bytes, alignment); |
if (allocation.IsRetry()) { |
FatalProcessOutOfMemory( |
"MarkCompactCollector: semi-space copy, fallback in old gen\n"); |
@@ -1710,10 +1724,9 @@ |
public: |
EvacuateOldSpaceVisitor(Heap* heap, |
CompactionSpaceCollection* compaction_spaces, |
- SlotsBuffer** evacuation_slots_buffer, |
- LocalStoreBuffer* local_store_buffer) |
- : EvacuateVisitorBase(heap, compaction_spaces, evacuation_slots_buffer, |
- local_store_buffer) {} |
+ SlotsBuffer** evacuation_slots_buffer) |
+ : EvacuateVisitorBase(heap, evacuation_slots_buffer), |
+ compaction_spaces_(compaction_spaces) {} |
bool Visit(HeapObject* object) override { |
CompactionSpace* target_space = compaction_spaces_->Get( |
@@ -1725,6 +1738,9 @@ |
} |
return false; |
} |
+ |
+ private: |
+ CompactionSpaceCollection* compaction_spaces_; |
}; |
@@ -2532,14 +2548,14 @@ |
heap()->set_encountered_transition_arrays(Smi::FromInt(0)); |
} |
+ |
void MarkCompactCollector::RecordMigratedSlot( |
- Object* value, Address slot, SlotsBuffer** evacuation_slots_buffer, |
- LocalStoreBuffer* local_store_buffer) { |
+ Object* value, Address slot, SlotsBuffer** evacuation_slots_buffer) { |
// When parallel compaction is in progress, store and slots buffer entries |
// require synchronization. |
if (heap_->InNewSpace(value)) { |
if (compaction_in_progress_) { |
- local_store_buffer->Record(slot); |
+ heap_->store_buffer()->MarkSynchronized(slot); |
} else { |
heap_->store_buffer()->Mark(slot); |
} |
@@ -2621,23 +2637,19 @@ |
class RecordMigratedSlotVisitor final : public ObjectVisitor { |
public: |
RecordMigratedSlotVisitor(MarkCompactCollector* collector, |
- SlotsBuffer** evacuation_slots_buffer, |
- LocalStoreBuffer* local_store_buffer) |
+ SlotsBuffer** evacuation_slots_buffer) |
: collector_(collector), |
- evacuation_slots_buffer_(evacuation_slots_buffer), |
- local_store_buffer_(local_store_buffer) {} |
+ evacuation_slots_buffer_(evacuation_slots_buffer) {} |
V8_INLINE void VisitPointer(Object** p) override { |
collector_->RecordMigratedSlot(*p, reinterpret_cast<Address>(p), |
- evacuation_slots_buffer_, |
- local_store_buffer_); |
+ evacuation_slots_buffer_); |
} |
V8_INLINE void VisitPointers(Object** start, Object** end) override { |
while (start < end) { |
collector_->RecordMigratedSlot(*start, reinterpret_cast<Address>(start), |
- evacuation_slots_buffer_, |
- local_store_buffer_); |
+ evacuation_slots_buffer_); |
++start; |
} |
} |
@@ -2653,7 +2665,6 @@ |
private: |
MarkCompactCollector* collector_; |
SlotsBuffer** evacuation_slots_buffer_; |
- LocalStoreBuffer* local_store_buffer_; |
}; |
@@ -2671,10 +2682,9 @@ |
// pointer iteration. This is an issue if the store buffer overflows and we |
// have to scan the entire old space, including dead objects, looking for |
// pointers to new space. |
-void MarkCompactCollector::MigrateObject(HeapObject* dst, HeapObject* src, |
- int size, AllocationSpace dest, |
- SlotsBuffer** evacuation_slots_buffer, |
- LocalStoreBuffer* local_store_buffer) { |
+void MarkCompactCollector::MigrateObject( |
+ HeapObject* dst, HeapObject* src, int size, AllocationSpace dest, |
+ SlotsBuffer** evacuation_slots_buffer) { |
Address dst_addr = dst->address(); |
Address src_addr = src->address(); |
DCHECK(heap()->AllowedToBeMigrated(src, dest)); |
@@ -2685,8 +2695,7 @@ |
DCHECK(IsAligned(size, kPointerSize)); |
heap()->MoveBlock(dst->address(), src->address(), size); |
- RecordMigratedSlotVisitor visitor(this, evacuation_slots_buffer, |
- local_store_buffer); |
+ RecordMigratedSlotVisitor visitor(this, evacuation_slots_buffer); |
dst->IterateBody(&visitor); |
} else if (dest == CODE_SPACE) { |
DCHECK_CODEOBJECT_SIZE(size, heap()->code_space()); |
@@ -3048,18 +3057,54 @@ |
void MarkCompactCollector::EvacuateNewSpacePrologue() { |
+ // There are soft limits in the allocation code, designed trigger a mark |
+ // sweep collection by failing allocations. But since we are already in |
+ // a mark-sweep allocation, there is no sense in trying to trigger one. |
+ AlwaysAllocateScope scope(isolate()); |
+ |
NewSpace* new_space = heap()->new_space(); |
- NewSpacePageIterator it(new_space->bottom(), new_space->top()); |
- // Append the list of new space pages to be processed. |
+ |
+ // Store allocation range before flipping semispaces. |
+ Address from_bottom = new_space->bottom(); |
+ Address from_top = new_space->top(); |
+ |
+ // Flip the semispaces. After flipping, to space is empty, from space has |
+ // live objects. |
+ new_space->Flip(); |
+ new_space->ResetAllocationInfo(); |
+ |
+ newspace_evacuation_candidates_.Clear(); |
+ NewSpacePageIterator it(from_bottom, from_top); |
while (it.has_next()) { |
newspace_evacuation_candidates_.Add(it.next()); |
} |
- new_space->Flip(); |
- new_space->ResetAllocationInfo(); |
-} |
- |
-void MarkCompactCollector::EvacuateNewSpaceEpilogue() { |
- newspace_evacuation_candidates_.Rewind(0); |
+} |
+ |
+ |
+HashMap* MarkCompactCollector::EvacuateNewSpaceInParallel() { |
+ HashMap* local_pretenuring_feedback = new HashMap( |
+ HashMap::PointersMatch, kInitialLocalPretenuringFeedbackCapacity); |
+ EvacuateNewSpaceVisitor new_space_visitor(heap(), &migration_slots_buffer_, |
+ local_pretenuring_feedback); |
+ // First pass: traverse all objects in inactive semispace, remove marks, |
+ // migrate live objects and write forwarding addresses. This stage puts |
+ // new entries in the store buffer and may cause some pages to be marked |
+ // scan-on-scavenge. |
+ for (int i = 0; i < newspace_evacuation_candidates_.length(); i++) { |
+ NewSpacePage* p = |
+ reinterpret_cast<NewSpacePage*>(newspace_evacuation_candidates_[i]); |
+ bool ok = VisitLiveObjects(p, &new_space_visitor, kClearMarkbits); |
+ USE(ok); |
+ DCHECK(ok); |
+ } |
+ heap_->IncrementPromotedObjectsSize( |
+ static_cast<int>(new_space_visitor.promoted_size())); |
+ heap_->IncrementSemiSpaceCopiedObjectSize( |
+ static_cast<int>(new_space_visitor.semispace_copied_size())); |
+ heap_->IncrementYoungSurvivorsCounter( |
+ static_cast<int>(new_space_visitor.promoted_size()) + |
+ static_cast<int>(new_space_visitor.semispace_copied_size())); |
+ return local_pretenuring_feedback; |
} |
@@ -3069,168 +3114,8 @@ |
evacuation_slots_buffers_.Add(evacuation_slots_buffer); |
} |
-class MarkCompactCollector::Evacuator : public Malloced { |
- public: |
- Evacuator(MarkCompactCollector* collector, |
- const List<Page*>& evacuation_candidates, |
- const List<NewSpacePage*>& newspace_evacuation_candidates) |
- : collector_(collector), |
- evacuation_candidates_(evacuation_candidates), |
- newspace_evacuation_candidates_(newspace_evacuation_candidates), |
- compaction_spaces_(collector->heap()), |
- local_slots_buffer_(nullptr), |
- local_store_buffer_(), |
- local_pretenuring_feedback_(HashMap::PointersMatch, |
- kInitialLocalPretenuringFeedbackCapacity), |
- new_space_visitor_(collector->heap(), &compaction_spaces_, |
- &local_slots_buffer_, &local_store_buffer_, |
- &local_pretenuring_feedback_), |
- old_space_visitor_(collector->heap(), &compaction_spaces_, |
- &local_slots_buffer_, &local_store_buffer_), |
- duration_(0.0), |
- bytes_compacted_(0), |
- task_id_(0) {} |
- |
- // Evacuate the configured set of pages in parallel. |
- inline void EvacuatePages(); |
- |
- // Merge back locally cached info sequentially. Note that this method needs |
- // to be called from the main thread. |
- inline void Finalize(); |
- |
- CompactionSpaceCollection* compaction_spaces() { return &compaction_spaces_; } |
- |
- uint32_t task_id() { return task_id_; } |
- void set_task_id(uint32_t id) { task_id_ = id; } |
- |
- private: |
- static const int kInitialLocalPretenuringFeedbackCapacity = 256; |
- |
- Heap* heap() { return collector_->heap(); } |
- |
- void ReportCompactionProgress(double duration, intptr_t bytes_compacted) { |
- duration_ += duration; |
- bytes_compacted_ += bytes_compacted; |
- } |
- |
- inline bool EvacuateSinglePage(MemoryChunk* p, HeapObjectVisitor* visitor); |
- |
- MarkCompactCollector* collector_; |
- |
- // Pages to process. |
- const List<Page*>& evacuation_candidates_; |
- const List<NewSpacePage*>& newspace_evacuation_candidates_; |
- |
- // Locally cached collector data. |
- CompactionSpaceCollection compaction_spaces_; |
- SlotsBuffer* local_slots_buffer_; |
- LocalStoreBuffer local_store_buffer_; |
- HashMap local_pretenuring_feedback_; |
- |
- // Vistors for the corresponding spaces. |
- EvacuateNewSpaceVisitor new_space_visitor_; |
- EvacuateOldSpaceVisitor old_space_visitor_; |
- |
- // Book keeping info. |
- double duration_; |
- intptr_t bytes_compacted_; |
- |
- // Task id, if this evacuator is executed on a background task instead of |
- // the main thread. Can be used to try to abort the task currently scheduled |
- // to executed to evacuate pages. |
- uint32_t task_id_; |
-}; |
- |
-bool MarkCompactCollector::Evacuator::EvacuateSinglePage( |
- MemoryChunk* p, HeapObjectVisitor* visitor) { |
- bool success = true; |
- if (p->parallel_compaction_state().TrySetValue( |
- MemoryChunk::kCompactingDone, MemoryChunk::kCompactingInProgress)) { |
- if (p->IsEvacuationCandidate() || p->InNewSpace()) { |
- DCHECK_EQ(p->parallel_compaction_state().Value(), |
- MemoryChunk::kCompactingInProgress); |
- int saved_live_bytes = p->LiveBytes(); |
- double evacuation_time; |
- { |
- AlwaysAllocateScope always_allocate(heap()->isolate()); |
- TimedScope timed_scope(&evacuation_time); |
- success = collector_->VisitLiveObjects(p, visitor, kClearMarkbits); |
- } |
- if (success) { |
- ReportCompactionProgress(evacuation_time, saved_live_bytes); |
- p->parallel_compaction_state().SetValue( |
- MemoryChunk::kCompactingFinalize); |
- } else { |
- p->parallel_compaction_state().SetValue( |
- MemoryChunk::kCompactingAborted); |
- } |
- } else { |
- // There could be popular pages in the list of evacuation candidates |
- // which we do not compact. |
- p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone); |
- } |
- } |
- return success; |
-} |
- |
-void MarkCompactCollector::Evacuator::EvacuatePages() { |
- for (NewSpacePage* p : newspace_evacuation_candidates_) { |
- DCHECK(p->InNewSpace()); |
- DCHECK_EQ(p->concurrent_sweeping_state().Value(), |
- NewSpacePage::kSweepingDone); |
- bool success = EvacuateSinglePage(p, &new_space_visitor_); |
- DCHECK(success); |
- USE(success); |
- } |
- for (Page* p : evacuation_candidates_) { |
- DCHECK(p->IsEvacuationCandidate() || |
- p->IsFlagSet(MemoryChunk::RESCAN_ON_EVACUATION)); |
- DCHECK_EQ(p->concurrent_sweeping_state().Value(), Page::kSweepingDone); |
- EvacuateSinglePage(p, &old_space_visitor_); |
- } |
-} |
- |
-void MarkCompactCollector::Evacuator::Finalize() { |
- heap()->old_space()->MergeCompactionSpace(compaction_spaces_.Get(OLD_SPACE)); |
- heap()->code_space()->MergeCompactionSpace( |
- compaction_spaces_.Get(CODE_SPACE)); |
- heap()->tracer()->AddCompactionEvent(duration_, bytes_compacted_); |
- heap()->IncrementPromotedObjectsSize(new_space_visitor_.promoted_size()); |
- heap()->IncrementSemiSpaceCopiedObjectSize( |
- new_space_visitor_.semispace_copied_size()); |
- heap()->IncrementYoungSurvivorsCounter( |
- new_space_visitor_.promoted_size() + |
- new_space_visitor_.semispace_copied_size()); |
- heap()->MergeAllocationSitePretenuringFeedback(local_pretenuring_feedback_); |
- local_store_buffer_.Process(heap()->store_buffer()); |
- collector_->AddEvacuationSlotsBufferSynchronized(local_slots_buffer_); |
-} |
- |
-class MarkCompactCollector::CompactionTask : public CancelableTask { |
- public: |
- explicit CompactionTask(Heap* heap, Evacuator* evacuator) |
- : CancelableTask(heap->isolate()), heap_(heap), evacuator_(evacuator) { |
- evacuator->set_task_id(id()); |
- } |
- |
- virtual ~CompactionTask() {} |
- |
- private: |
- // v8::internal::CancelableTask overrides. |
- void RunInternal() override { |
- evacuator_->EvacuatePages(); |
- heap_->mark_compact_collector() |
- ->pending_compaction_tasks_semaphore_.Signal(); |
- } |
- |
- Heap* heap_; |
- Evacuator* evacuator_; |
- |
- DISALLOW_COPY_AND_ASSIGN(CompactionTask); |
-}; |
- |
-int MarkCompactCollector::NumberOfParallelCompactionTasks(int pages, |
- intptr_t live_bytes) { |
+ |
+int MarkCompactCollector::NumberOfParallelCompactionTasks() { |
if (!FLAG_parallel_compaction) return 1; |
// Compute the number of needed tasks based on a target compaction time, the |
// profiled compaction speed and marked live memory. |
@@ -3238,83 +3123,83 @@ |
// The number of parallel compaction tasks is limited by: |
// - #evacuation pages |
// - (#cores - 1) |
+ // - a hard limit |
const double kTargetCompactionTimeInMs = 1; |
- const int kNumSweepingTasks = 3; |
+ const int kMaxCompactionTasks = 8; |
intptr_t compaction_speed = |
heap()->tracer()->CompactionSpeedInBytesPerMillisecond(); |
- |
- const int available_cores = |
- Max(1, base::SysInfo::NumberOfProcessors() - kNumSweepingTasks - 1); |
- int tasks; |
- if (compaction_speed > 0) { |
- tasks = 1 + static_cast<int>(static_cast<double>(live_bytes) / |
- compaction_speed / kTargetCompactionTimeInMs); |
- } else { |
- tasks = pages; |
- } |
- const int tasks_capped_pages = Min(pages, tasks); |
- return Min(available_cores, tasks_capped_pages); |
-} |
- |
- |
-void MarkCompactCollector::EvacuatePagesInParallel() { |
- int num_pages = 0; |
+ if (compaction_speed == 0) return 1; |
+ |
intptr_t live_bytes = 0; |
for (Page* page : evacuation_candidates_) { |
- num_pages++; |
live_bytes += page->LiveBytes(); |
} |
- for (NewSpacePage* page : newspace_evacuation_candidates_) { |
- num_pages++; |
- live_bytes += page->LiveBytes(); |
- } |
- DCHECK_GE(num_pages, 1); |
+ |
+ const int cores = Max(1, base::SysInfo::NumberOfProcessors() - 1); |
+ const int tasks = |
+ 1 + static_cast<int>(static_cast<double>(live_bytes) / compaction_speed / |
+ kTargetCompactionTimeInMs); |
+ const int tasks_capped_pages = Min(evacuation_candidates_.length(), tasks); |
+ const int tasks_capped_cores = Min(cores, tasks_capped_pages); |
+ const int tasks_capped_hard = Min(kMaxCompactionTasks, tasks_capped_cores); |
+ return tasks_capped_hard; |
+} |
+ |
+ |
+void MarkCompactCollector::EvacuatePagesInParallel() { |
+ const int num_pages = evacuation_candidates_.length(); |
+ if (num_pages == 0) return; |
// Used for trace summary. |
+ intptr_t live_bytes = 0; |
intptr_t compaction_speed = 0; |
if (FLAG_trace_fragmentation) { |
+ for (Page* page : evacuation_candidates_) { |
+ live_bytes += page->LiveBytes(); |
+ } |
compaction_speed = heap()->tracer()->CompactionSpeedInBytesPerMillisecond(); |
} |
- |
- const int num_tasks = NumberOfParallelCompactionTasks(num_pages, live_bytes); |
+ const int num_tasks = NumberOfParallelCompactionTasks(); |
// Set up compaction spaces. |
- Evacuator** evacuators = new Evacuator*[num_tasks]; |
CompactionSpaceCollection** compaction_spaces_for_tasks = |
new CompactionSpaceCollection*[num_tasks]; |
for (int i = 0; i < num_tasks; i++) { |
- evacuators[i] = new Evacuator(this, evacuation_candidates_, |
- newspace_evacuation_candidates_); |
- compaction_spaces_for_tasks[i] = evacuators[i]->compaction_spaces(); |
- } |
+ compaction_spaces_for_tasks[i] = new CompactionSpaceCollection(heap()); |
+ } |
+ |
heap()->old_space()->DivideUponCompactionSpaces(compaction_spaces_for_tasks, |
num_tasks); |
heap()->code_space()->DivideUponCompactionSpaces(compaction_spaces_for_tasks, |
num_tasks); |
+ |
+ uint32_t* task_ids = new uint32_t[num_tasks - 1]; |
+ // Kick off parallel tasks. |
+ StartParallelCompaction(compaction_spaces_for_tasks, task_ids, num_tasks); |
+ // Wait for unfinished and not-yet-started tasks. |
+ WaitUntilCompactionCompleted(task_ids, num_tasks - 1); |
+ delete[] task_ids; |
+ |
+ double compaction_duration = 0.0; |
+ intptr_t compacted_memory = 0; |
+ // Merge back memory (compacted and unused) from compaction spaces. |
+ for (int i = 0; i < num_tasks; i++) { |
+ heap()->old_space()->MergeCompactionSpace( |
+ compaction_spaces_for_tasks[i]->Get(OLD_SPACE)); |
+ heap()->code_space()->MergeCompactionSpace( |
+ compaction_spaces_for_tasks[i]->Get(CODE_SPACE)); |
+ compacted_memory += compaction_spaces_for_tasks[i]->bytes_compacted(); |
+ compaction_duration += compaction_spaces_for_tasks[i]->duration(); |
+ delete compaction_spaces_for_tasks[i]; |
+ } |
delete[] compaction_spaces_for_tasks; |
- |
- // Kick off parallel tasks. |
- StartParallelCompaction(evacuators, num_tasks); |
- // Wait for unfinished and not-yet-started tasks. |
- WaitUntilCompactionCompleted(&evacuators[1], num_tasks - 1); |
- |
- // Finalize local evacuators by merging back all locally cached data. |
- for (int i = 0; i < num_tasks; i++) { |
- evacuators[i]->Finalize(); |
- delete evacuators[i]; |
- } |
- delete[] evacuators; |
- |
- // Finalize pages sequentially. |
- for (NewSpacePage* p : newspace_evacuation_candidates_) { |
- DCHECK_EQ(p->parallel_compaction_state().Value(), |
- MemoryChunk::kCompactingFinalize); |
- p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone); |
- } |
- |
+ heap()->tracer()->AddCompactionEvent(compaction_duration, compacted_memory); |
+ |
+ // Finalize sequentially. |
int abandoned_pages = 0; |
- for (Page* p : evacuation_candidates_) { |
+ for (int i = 0; i < num_pages; i++) { |
+ Page* p = evacuation_candidates_[i]; |
switch (p->parallel_compaction_state().Value()) { |
case MemoryChunk::ParallelCompactingState::kCompactingAborted: |
// We have partially compacted the page, i.e., some objects may have |
@@ -3347,7 +3232,7 @@ |
DCHECK(p->IsFlagSet(Page::RESCAN_ON_EVACUATION)); |
break; |
default: |
- // MemoryChunk::kCompactingInProgress. |
+ // We should not observe kCompactingInProgress, or kCompactingDone. |
UNREACHABLE(); |
} |
p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone); |
@@ -3364,32 +3249,72 @@ |
} |
} |
-void MarkCompactCollector::StartParallelCompaction(Evacuator** evacuators, |
- int len) { |
+ |
+void MarkCompactCollector::StartParallelCompaction( |
+ CompactionSpaceCollection** compaction_spaces, uint32_t* task_ids, |
+ int len) { |
compaction_in_progress_ = true; |
for (int i = 1; i < len; i++) { |
- CompactionTask* task = new CompactionTask(heap(), evacuators[i]); |
+ CompactionTask* task = new CompactionTask(heap(), compaction_spaces[i]); |
+ task_ids[i - 1] = task->id(); |
V8::GetCurrentPlatform()->CallOnBackgroundThread( |
task, v8::Platform::kShortRunningTask); |
} |
- // Contribute on main thread. |
- evacuators[0]->EvacuatePages(); |
-} |
- |
-void MarkCompactCollector::WaitUntilCompactionCompleted(Evacuator** evacuators, |
+ // Contribute in main thread. |
+ EvacuatePages(compaction_spaces[0], &migration_slots_buffer_); |
+} |
+ |
+ |
+void MarkCompactCollector::WaitUntilCompactionCompleted(uint32_t* task_ids, |
int len) { |
// Try to cancel compaction tasks that have not been run (as they might be |
// stuck in a worker queue). Tasks that cannot be canceled, have either |
// already completed or are still running, hence we need to wait for their |
// semaphore signal. |
for (int i = 0; i < len; i++) { |
- if (!heap()->isolate()->cancelable_task_manager()->TryAbort( |
- evacuators[i]->task_id())) { |
+ if (!heap()->isolate()->cancelable_task_manager()->TryAbort(task_ids[i])) { |
pending_compaction_tasks_semaphore_.Wait(); |
} |
} |
compaction_in_progress_ = false; |
+} |
+ |
+ |
+void MarkCompactCollector::EvacuatePages( |
+ CompactionSpaceCollection* compaction_spaces, |
+ SlotsBuffer** evacuation_slots_buffer) { |
+ EvacuateOldSpaceVisitor visitor(heap(), compaction_spaces, |
+ evacuation_slots_buffer); |
+ for (int i = 0; i < evacuation_candidates_.length(); i++) { |
+ Page* p = evacuation_candidates_[i]; |
+ DCHECK(p->IsEvacuationCandidate() || |
+ p->IsFlagSet(Page::RESCAN_ON_EVACUATION)); |
+ DCHECK(p->SweepingDone()); |
+ if (p->parallel_compaction_state().TrySetValue( |
+ MemoryChunk::kCompactingDone, MemoryChunk::kCompactingInProgress)) { |
+ if (p->IsEvacuationCandidate()) { |
+ DCHECK_EQ(p->parallel_compaction_state().Value(), |
+ MemoryChunk::kCompactingInProgress); |
+ double start = heap()->MonotonicallyIncreasingTimeInMs(); |
+ intptr_t live_bytes = p->LiveBytes(); |
+ AlwaysAllocateScope always_allocate(isolate()); |
+ if (VisitLiveObjects(p, &visitor, kClearMarkbits)) { |
+ p->parallel_compaction_state().SetValue( |
+ MemoryChunk::kCompactingFinalize); |
+ compaction_spaces->ReportCompactionProgress( |
+ heap()->MonotonicallyIncreasingTimeInMs() - start, live_bytes); |
+ } else { |
+ p->parallel_compaction_state().SetValue( |
+ MemoryChunk::kCompactingAborted); |
+ } |
+ } else { |
+ // There could be popular pages in the list of evacuation candidates |
+ // which we do compact. |
+ p->parallel_compaction_state().SetValue(MemoryChunk::kCompactingDone); |
+ } |
+ } |
+ } |
} |
@@ -3535,7 +3460,9 @@ |
Address end_slot) { |
// Remove entries by replacing them with an old-space slot containing a smi |
// that is located in an unmovable page. |
- for (Page* p : evacuation_candidates_) { |
+ int npages = evacuation_candidates_.length(); |
+ for (int i = 0; i < npages; i++) { |
+ Page* p = evacuation_candidates_[i]; |
DCHECK(p->IsEvacuationCandidate() || |
p->IsFlagSet(Page::RESCAN_ON_EVACUATION)); |
if (p->IsEvacuationCandidate()) { |
@@ -3615,7 +3542,8 @@ |
void MarkCompactCollector::SweepAbortedPages() { |
// Second pass on aborted pages. |
- for (Page* p : evacuation_candidates_) { |
+ for (int i = 0; i < evacuation_candidates_.length(); i++) { |
+ Page* p = evacuation_candidates_[i]; |
if (p->IsFlagSet(Page::COMPACTION_WAS_ABORTED)) { |
p->ClearFlag(MemoryChunk::COMPACTION_WAS_ABORTED); |
p->concurrent_sweeping_state().SetValue(Page::kSweepingInProgress); |
@@ -3647,15 +3575,26 @@ |
GCTracer::Scope gc_scope(heap()->tracer(), GCTracer::Scope::MC_EVACUATE); |
Heap::RelocationLock relocation_lock(heap()); |
+ HashMap* local_pretenuring_feedback = nullptr; |
{ |
GCTracer::Scope gc_scope(heap()->tracer(), |
GCTracer::Scope::MC_EVACUATE_NEW_SPACE); |
EvacuationScope evacuation_scope(this); |
- |
EvacuateNewSpacePrologue(); |
+ local_pretenuring_feedback = EvacuateNewSpaceInParallel(); |
+ heap_->new_space()->set_age_mark(heap_->new_space()->top()); |
+ } |
+ |
+ { |
+ GCTracer::Scope gc_scope(heap()->tracer(), |
+ GCTracer::Scope::MC_EVACUATE_CANDIDATES); |
+ EvacuationScope evacuation_scope(this); |
EvacuatePagesInParallel(); |
- EvacuateNewSpaceEpilogue(); |
- heap()->new_space()->set_age_mark(heap()->new_space()->top()); |
+ } |
+ |
+ { |
+ heap_->MergeAllocationSitePretenuringFeedback(*local_pretenuring_feedback); |
+ delete local_pretenuring_feedback; |
} |
UpdatePointersAfterEvacuation(); |
@@ -3732,11 +3671,13 @@ |
heap_->store_buffer()->IteratePointersToNewSpace(&UpdatePointer); |
} |
+ int npages = evacuation_candidates_.length(); |
{ |
GCTracer::Scope gc_scope( |
heap()->tracer(), |
GCTracer::Scope::MC_EVACUATE_UPDATE_POINTERS_BETWEEN_EVACUATED); |
- for (Page* p : evacuation_candidates_) { |
+ for (int i = 0; i < npages; i++) { |
+ Page* p = evacuation_candidates_[i]; |
DCHECK(p->IsEvacuationCandidate() || |
p->IsFlagSet(Page::RESCAN_ON_EVACUATION)); |
@@ -3811,7 +3752,9 @@ |
void MarkCompactCollector::ReleaseEvacuationCandidates() { |
- for (Page* p : evacuation_candidates_) { |
+ int npages = evacuation_candidates_.length(); |
+ for (int i = 0; i < npages; i++) { |
+ Page* p = evacuation_candidates_[i]; |
if (!p->IsEvacuationCandidate()) continue; |
PagedSpace* space = static_cast<PagedSpace*>(p->owner()); |
space->Free(p->area_start(), p->area_size()); |