5 #include "src/heap/sweeper.h" 7 #include "src/base/template-utils.h" 8 #include "src/heap/array-buffer-tracker-inl.h" 9 #include "src/heap/gc-tracer.h" 10 #include "src/heap/mark-compact-inl.h" 11 #include "src/heap/remembered-set.h" 12 #include "src/objects-inl.h" 13 #include "src/vm-state-inl.h" 18 Sweeper::Sweeper(Heap* heap, MajorNonAtomicMarkingState* marking_state)
20 marking_state_(marking_state),
22 pending_sweeper_tasks_semaphore_(0),
23 incremental_sweeper_pending_(false),
24 sweeping_in_progress_(false),
25 num_sweeping_tasks_(0),
26 stop_sweeper_tasks_(false),
27 iterability_task_semaphore_(0),
28 iterability_in_progress_(false),
29 iterability_task_started_(false),
30 should_reduce_memory_(false) {}
32 Sweeper::PauseOrCompleteScope::PauseOrCompleteScope(Sweeper* sweeper)
34 sweeper_->stop_sweeper_tasks_ =
true;
35 if (!sweeper_->sweeping_in_progress())
return;
37 sweeper_->AbortAndWaitForTasks();
40 if (sweeper_->IsDoneSweeping()) {
41 sweeper_->heap_->mark_compact_collector()->EnsureSweepingCompleted();
42 DCHECK(!sweeper_->sweeping_in_progress());
46 DCHECK(sweeper_->sweeping_in_progress());
50 Sweeper::PauseOrCompleteScope::~PauseOrCompleteScope() {
51 sweeper_->stop_sweeper_tasks_ =
false;
52 if (!sweeper_->sweeping_in_progress())
return;
54 sweeper_->StartSweeperTasks();
57 Sweeper::FilterSweepingPagesScope::FilterSweepingPagesScope(
58 Sweeper* sweeper,
const PauseOrCompleteScope& pause_or_complete_scope)
60 pause_or_complete_scope_(pause_or_complete_scope),
61 sweeping_in_progress_(sweeper_->sweeping_in_progress()) {
62 USE(pause_or_complete_scope_);
63 if (!sweeping_in_progress_)
return;
65 int old_space_index = GetSweepSpaceIndex(OLD_SPACE);
66 old_space_sweeping_list_ =
67 std::move(sweeper_->sweeping_list_[old_space_index]);
68 sweeper_->sweeping_list_[old_space_index].clear();
71 Sweeper::FilterSweepingPagesScope::~FilterSweepingPagesScope() {
72 DCHECK_EQ(sweeping_in_progress_, sweeper_->sweeping_in_progress());
73 if (!sweeping_in_progress_)
return;
75 sweeper_->sweeping_list_[GetSweepSpaceIndex(OLD_SPACE)] =
76 std::move(old_space_sweeping_list_);
84 std::atomic<intptr_t>* num_sweeping_tasks,
85 AllocationSpace space_to_start)
88 pending_sweeper_tasks_(pending_sweeper_tasks),
89 num_sweeping_tasks_(num_sweeping_tasks),
90 space_to_start_(space_to_start),
91 tracer_(isolate->heap()->tracer()) {}
96 void RunInternal()
final {
97 TRACE_BACKGROUND_GC(tracer_,
98 GCTracer::BackgroundScope::MC_BACKGROUND_SWEEPING);
99 DCHECK(IsValidSweepingSpace(space_to_start_));
100 const int offset = space_to_start_ - FIRST_GROWABLE_PAGED_SPACE;
101 for (
int i = 0;
i < kNumberOfSweepingSpaces;
i++) {
102 const AllocationSpace space_id =
static_cast<AllocationSpace
>(
103 FIRST_GROWABLE_PAGED_SPACE +
104 ((
i + offset) % kNumberOfSweepingSpaces));
106 if (space_id == CODE_SPACE)
continue;
107 DCHECK(IsValidSweepingSpace(space_id));
108 sweeper_->SweepSpaceFromTask(space_id);
110 (*num_sweeping_tasks_)--;
111 pending_sweeper_tasks_->Signal();
116 std::atomic<intptr_t>*
const num_sweeping_tasks_;
117 AllocationSpace space_to_start_;
126 :
CancelableTask(isolate), isolate_(isolate), sweeper_(sweeper) {}
131 void RunInternal()
final {
133 TRACE_EVENT_CALL_STATS_SCOPED(isolate_,
"v8",
"V8.Task");
135 sweeper_->incremental_sweeper_pending_ =
false;
137 if (sweeper_->sweeping_in_progress()) {
138 if (!sweeper_->SweepSpaceIncrementallyFromTask(CODE_SPACE)) {
139 sweeper_->ScheduleIncrementalSweepingTask();
149 void Sweeper::StartSweeping() {
150 CHECK(!stop_sweeper_tasks_);
151 sweeping_in_progress_ =
true;
152 iterability_in_progress_ =
true;
153 should_reduce_memory_ = heap_->ShouldReduceMemory();
155 heap_->mark_compact_collector()->non_atomic_marking_state();
156 ForAllSweepingSpaces([
this, marking_state](AllocationSpace space) {
157 int space_index = GetSweepSpaceIndex(space);
158 std::sort(sweeping_list_[space_index].begin(),
159 sweeping_list_[space_index].end(),
161 return marking_state->live_bytes(a) <
162 marking_state->live_bytes(b);
167 void Sweeper::StartSweeperTasks() {
168 DCHECK_EQ(0, num_tasks_);
169 DCHECK_EQ(0, num_sweeping_tasks_);
170 if (FLAG_concurrent_sweeping && sweeping_in_progress_ &&
171 !heap_->delay_sweeper_tasks_for_testing_) {
172 ForAllSweepingSpaces([
this](AllocationSpace space) {
173 DCHECK(IsValidSweepingSpace(space));
174 num_sweeping_tasks_++;
175 auto task = base::make_unique<SweeperTask>(
176 heap_->isolate(),
this, &pending_sweeper_tasks_semaphore_,
177 &num_sweeping_tasks_, space);
178 DCHECK_LT(num_tasks_, kMaxSweeperTasks);
179 task_ids_[num_tasks_++] = task->id();
180 V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
182 ScheduleIncrementalSweepingTask();
186 void Sweeper::SweepOrWaitUntilSweepingCompleted(Page* page) {
187 if (!page->SweepingDone()) {
188 ParallelSweepPage(page, page->owner()->identity());
189 if (!page->SweepingDone()) {
193 page->WaitUntilSweepingCompleted();
198 Page* Sweeper::GetSweptPageSafe(PagedSpace* space) {
199 base::MutexGuard guard(&mutex_);
200 SweptList& list = swept_list_[GetSweepSpaceIndex(space->identity())];
202 auto last_page = list.back();
209 void Sweeper::AbortAndWaitForTasks() {
210 if (!FLAG_concurrent_sweeping)
return;
212 for (
int i = 0;
i < num_tasks_;
i++) {
213 if (heap_->isolate()->cancelable_task_manager()->TryAbort(task_ids_[
i]) !=
214 TryAbortResult::kTaskAborted) {
215 pending_sweeper_tasks_semaphore_.Wait();
218 num_sweeping_tasks_--;
222 DCHECK_EQ(0, num_sweeping_tasks_);
225 void Sweeper::EnsureCompleted() {
226 if (!sweeping_in_progress_)
return;
228 EnsureIterabilityCompleted();
232 ForAllSweepingSpaces(
233 [
this](AllocationSpace space) { ParallelSweepSpace(space, 0); });
235 AbortAndWaitForTasks();
237 ForAllSweepingSpaces([
this](AllocationSpace space) {
238 CHECK(sweeping_list_[GetSweepSpaceIndex(space)].empty());
240 sweeping_in_progress_ =
false;
243 bool Sweeper::AreSweeperTasksRunning() {
return num_sweeping_tasks_ != 0; }
245 int Sweeper::RawSweep(Page* p, FreeListRebuildingMode free_list_mode,
246 FreeSpaceTreatmentMode free_space_mode) {
247 Space* space = p->owner();
248 DCHECK_NOT_NULL(space);
249 DCHECK(free_list_mode == IGNORE_FREE_LIST || space->identity() == OLD_SPACE ||
250 space->identity() == CODE_SPACE || space->identity() == MAP_SPACE);
251 DCHECK(!p->IsEvacuationCandidate() && !p->SweepingDone());
256 bool non_empty_typed_slots = p->typed_slot_set<OLD_TO_NEW>() !=
nullptr ||
257 p->typed_slot_set<OLD_TO_OLD>() !=
nullptr;
260 std::map<uint32_t, uint32_t> free_ranges;
264 ArrayBufferTracker::FreeDead(p, marking_state_);
266 Address free_start = p->area_start();
271 const bool rebuild_skip_list =
272 space->identity() == CODE_SPACE && p->skip_list() !=
nullptr;
273 SkipList* skip_list = p->skip_list();
274 if (rebuild_skip_list) {
278 intptr_t live_bytes = 0;
279 intptr_t freed_bytes = 0;
280 intptr_t max_freed_bytes = 0;
281 int curr_region = -1;
285 p->ResetAllocatedBytes();
287 for (
auto object_and_size :
288 LiveObjectRange<kBlackObjects>(p, marking_state_->bitmap(p))) {
289 HeapObject*
const object = object_and_size.first;
290 DCHECK(marking_state_->IsBlack(
object));
291 Address free_end =
object->address();
292 if (free_end != free_start) {
293 CHECK_GT(free_end, free_start);
294 size_t size =
static_cast<size_t>(free_end - free_start);
295 if (free_space_mode == ZAP_FREE_SPACE) {
296 ZapCode(free_start, size);
298 if (free_list_mode == REBUILD_FREE_LIST) {
299 freed_bytes =
reinterpret_cast<PagedSpace*
>(space)->Free(
300 free_start, size, SpaceAccountingMode::kSpaceUnaccounted);
301 max_freed_bytes = Max(freed_bytes, max_freed_bytes);
303 p->heap()->CreateFillerObjectAt(
304 free_start, static_cast<int>(size), ClearRecordedSlots::kNo,
305 ClearFreedMemoryMode::kClearFreedMemory);
307 if (should_reduce_memory_) p->DiscardUnusedMemory(free_start, size);
308 RememberedSet<OLD_TO_NEW>::RemoveRange(p, free_start, free_end,
309 SlotSet::KEEP_EMPTY_BUCKETS);
310 RememberedSet<OLD_TO_OLD>::RemoveRange(p, free_start, free_end,
311 SlotSet::KEEP_EMPTY_BUCKETS);
312 if (non_empty_typed_slots) {
313 free_ranges.insert(std::pair<uint32_t, uint32_t>(
314 static_cast<uint32_t>(free_start - p->address()),
315 static_cast<uint32_t>(free_end - p->address())));
318 Map map =
object->synchronized_map();
319 int size =
object->SizeFromMap(map);
321 if (rebuild_skip_list) {
322 int new_region_start = SkipList::RegionNumber(free_end);
324 SkipList::RegionNumber(free_end + size - kPointerSize);
325 if (new_region_start != curr_region || new_region_end != curr_region) {
326 skip_list->AddObject(free_end, size);
327 curr_region = new_region_end;
330 free_start = free_end + size;
333 if (free_start != p->area_end()) {
334 CHECK_GT(p->area_end(), free_start);
335 size_t size =
static_cast<size_t>(p->area_end() - free_start);
336 if (free_space_mode == ZAP_FREE_SPACE) {
337 ZapCode(free_start, size);
339 if (free_list_mode == REBUILD_FREE_LIST) {
340 freed_bytes =
reinterpret_cast<PagedSpace*
>(space)->Free(
341 free_start, size, SpaceAccountingMode::kSpaceUnaccounted);
342 max_freed_bytes = Max(freed_bytes, max_freed_bytes);
344 p->heap()->CreateFillerObjectAt(free_start, static_cast<int>(size),
345 ClearRecordedSlots::kNo,
346 ClearFreedMemoryMode::kClearFreedMemory);
348 if (should_reduce_memory_) p->DiscardUnusedMemory(free_start, size);
349 RememberedSet<OLD_TO_NEW>::RemoveRange(p, free_start, p->area_end(),
350 SlotSet::KEEP_EMPTY_BUCKETS);
351 RememberedSet<OLD_TO_OLD>::RemoveRange(p, free_start, p->area_end(),
352 SlotSet::KEEP_EMPTY_BUCKETS);
353 if (non_empty_typed_slots) {
354 free_ranges.insert(std::pair<uint32_t, uint32_t>(
355 static_cast<uint32_t>(free_start - p->address()),
356 static_cast<uint32_t>(p->area_end() - p->address())));
361 if (!free_ranges.empty()) {
362 TypedSlotSet* old_to_new = p->typed_slot_set<OLD_TO_NEW>();
363 if (old_to_new !=
nullptr) {
364 old_to_new->ClearInvalidSlots(free_ranges);
366 TypedSlotSet* old_to_old = p->typed_slot_set<OLD_TO_OLD>();
367 if (old_to_old !=
nullptr) {
368 old_to_old->ClearInvalidSlots(free_ranges);
372 marking_state_->bitmap(p)->Clear();
373 if (free_list_mode == IGNORE_FREE_LIST) {
374 marking_state_->SetLiveBytes(p, 0);
376 intptr_t freed_bytes = p->area_size() - live_bytes;
377 p->DecreaseAllocatedBytes(freed_bytes);
382 DCHECK_EQ(live_bytes, p->allocated_bytes());
384 p->set_concurrent_sweeping_state(Page::kSweepingDone);
385 if (free_list_mode == IGNORE_FREE_LIST)
return 0;
386 return static_cast<int>(FreeList::GuaranteedAllocatable(max_freed_bytes));
389 void Sweeper::SweepSpaceFromTask(AllocationSpace identity) {
390 Page* page =
nullptr;
391 while (!stop_sweeper_tasks_ &&
392 ((page = GetSweepingPageSafe(identity)) !=
nullptr)) {
393 ParallelSweepPage(page, identity);
397 bool Sweeper::SweepSpaceIncrementallyFromTask(AllocationSpace identity) {
398 if (Page* page = GetSweepingPageSafe(identity)) {
399 ParallelSweepPage(page, identity);
401 return sweeping_list_[GetSweepSpaceIndex(identity)].empty();
404 int Sweeper::ParallelSweepSpace(AllocationSpace identity,
405 int required_freed_bytes,
int max_pages) {
408 Page* page =
nullptr;
409 while ((page = GetSweepingPageSafe(identity)) !=
nullptr) {
410 int freed = ParallelSweepPage(page, identity);
413 max_freed = Max(max_freed, freed);
414 if ((required_freed_bytes) > 0 && (max_freed >= required_freed_bytes))
416 if ((max_pages > 0) && (pages_freed >= max_pages))
return max_freed;
421 int Sweeper::ParallelSweepPage(Page* page, AllocationSpace identity) {
424 if (page->SweepingDone())
return 0;
426 DCHECK(IsValidSweepingSpace(identity));
429 base::MutexGuard guard(page->mutex());
431 if (page->SweepingDone())
return 0;
435 CodePageMemoryModificationScope code_page_scope(page);
437 DCHECK_EQ(Page::kSweepingPending, page->concurrent_sweeping_state());
438 page->set_concurrent_sweeping_state(Page::kSweepingInProgress);
439 const FreeSpaceTreatmentMode free_space_mode =
440 Heap::ShouldZapGarbage() ? ZAP_FREE_SPACE : IGNORE_FREE_SPACE;
441 max_freed = RawSweep(page, REBUILD_FREE_LIST, free_space_mode);
442 DCHECK(page->SweepingDone());
445 TypedSlotSet* typed_slot_set = page->typed_slot_set<OLD_TO_NEW>();
446 if (typed_slot_set) {
447 typed_slot_set->FreeToBeFreedChunks();
449 SlotSet* slot_set = page->slot_set<OLD_TO_NEW>();
451 slot_set->FreeToBeFreedBuckets();
456 base::MutexGuard guard(&mutex_);
457 swept_list_[GetSweepSpaceIndex(identity)].push_back(page);
462 void Sweeper::ScheduleIncrementalSweepingTask() {
463 if (!incremental_sweeper_pending_) {
464 incremental_sweeper_pending_ =
true;
465 v8::Isolate* isolate =
reinterpret_cast<v8::Isolate*
>(heap_->isolate());
467 V8::GetCurrentPlatform()->GetForegroundTaskRunner(isolate);
468 taskrunner->PostTask(
469 base::make_unique<IncrementalSweeperTask>(heap_->isolate(),
this));
473 void Sweeper::AddPage(AllocationSpace space, Page* page,
474 Sweeper::AddPageMode mode) {
475 base::MutexGuard guard(&mutex_);
476 DCHECK(IsValidSweepingSpace(space));
477 DCHECK(!FLAG_concurrent_sweeping || !AreSweeperTasksRunning());
478 if (mode == Sweeper::REGULAR) {
479 PrepareToBeSweptPage(space, page);
483 DCHECK_EQ(Sweeper::READD_TEMPORARY_REMOVED_PAGE, mode);
485 DCHECK_EQ(Page::kSweepingPending, page->concurrent_sweeping_state());
486 sweeping_list_[GetSweepSpaceIndex(space)].push_back(page);
489 void Sweeper::PrepareToBeSweptPage(AllocationSpace space, Page* page) {
490 DCHECK_GE(page->area_size(),
491 static_cast<size_t>(marking_state_->live_bytes(page)));
492 DCHECK_EQ(Page::kSweepingDone, page->concurrent_sweeping_state());
493 page->ForAllFreeListCategories(
494 [](FreeListCategory* category) { DCHECK(!category->is_linked()); });
495 page->set_concurrent_sweeping_state(Page::kSweepingPending);
496 heap_->paged_space(space)->IncreaseAllocatedBytes(
497 marking_state_->live_bytes(page), page);
500 Page* Sweeper::GetSweepingPageSafe(AllocationSpace space) {
501 base::MutexGuard guard(&mutex_);
502 DCHECK(IsValidSweepingSpace(space));
503 int space_index = GetSweepSpaceIndex(space);
504 Page* page =
nullptr;
505 if (!sweeping_list_[space_index].empty()) {
506 page = sweeping_list_[space_index].front();
507 sweeping_list_[space_index].pop_front();
512 void Sweeper::EnsurePageIsIterable(Page* page) {
513 AllocationSpace space = page->owner()->identity();
514 if (IsValidSweepingSpace(space)) {
515 SweepOrWaitUntilSweepingCompleted(page);
517 DCHECK(IsValidIterabilitySpace(space));
518 EnsureIterabilityCompleted();
522 void Sweeper::EnsureIterabilityCompleted() {
523 if (!iterability_in_progress_)
return;
525 if (FLAG_concurrent_sweeping && iterability_task_started_) {
526 if (heap_->isolate()->cancelable_task_manager()->TryAbort(
527 iterability_task_id_) != TryAbortResult::kTaskAborted) {
528 iterability_task_semaphore_.Wait();
530 iterability_task_started_ =
false;
533 for (Page* page : iterability_list_) {
536 iterability_list_.clear();
537 iterability_in_progress_ =
false;
546 pending_iterability_task_(pending_iterability_task),
547 tracer_(isolate->heap()->tracer()) {}
552 void RunInternal()
final {
553 TRACE_BACKGROUND_GC(tracer_,
554 GCTracer::BackgroundScope::MC_BACKGROUND_SWEEPING);
555 for (
Page* page : sweeper_->iterability_list_) {
556 sweeper_->MakeIterable(page);
558 sweeper_->iterability_list_.clear();
559 pending_iterability_task_->Signal();
569 void Sweeper::StartIterabilityTasks() {
570 if (!iterability_in_progress_)
return;
572 DCHECK(!iterability_task_started_);
573 if (FLAG_concurrent_sweeping && !iterability_list_.empty()) {
574 auto task = base::make_unique<IterabilityTask>(
575 heap_->isolate(),
this, &iterability_task_semaphore_);
576 iterability_task_id_ = task->id();
577 iterability_task_started_ =
true;
578 V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
582 void Sweeper::AddPageForIterability(Page* page) {
583 DCHECK(sweeping_in_progress_);
584 DCHECK(iterability_in_progress_);
585 DCHECK(!iterability_task_started_);
586 DCHECK(IsValidIterabilitySpace(page->owner()->identity()));
587 DCHECK_EQ(Page::kSweepingDone, page->concurrent_sweeping_state());
589 iterability_list_.push_back(page);
590 page->set_concurrent_sweeping_state(Page::kSweepingPending);
593 void Sweeper::MakeIterable(Page* page) {
594 DCHECK(IsValidIterabilitySpace(page->owner()->identity()));
595 const FreeSpaceTreatmentMode free_space_mode =
596 Heap::ShouldZapGarbage() ? ZAP_FREE_SPACE : IGNORE_FREE_SPACE;
597 RawSweep(page, IGNORE_FREE_LIST, free_space_mode);