1 // Copyright 2012 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/heap/mark-compact.h" 6 7 #include "src/base/atomicops.h" 8 #include "src/base/bits.h" 9 #include "src/base/sys-info.h" 10 #include "src/code-stubs.h" 11 #include "src/compilation-cache.h" 12 #include "src/deoptimizer.h" 13 #include "src/execution.h" 14 #include "src/frames-inl.h" 15 #include "src/gdb-jit.h" 16 #include "src/global-handles.h" 17 #include "src/heap/array-buffer-tracker.h" 18 #include "src/heap/gc-tracer.h" 19 #include "src/heap/incremental-marking.h" 20 #include "src/heap/mark-compact-inl.h" 21 #include "src/heap/object-stats.h" 22 #include "src/heap/objects-visiting-inl.h" 23 #include "src/heap/objects-visiting.h" 24 #include "src/heap/page-parallel-job.h" 25 #include "src/heap/spaces-inl.h" 26 #include "src/ic/ic.h" 27 #include "src/ic/stub-cache.h" 28 #include "src/tracing/tracing-category-observer.h" 29 #include "src/utils-inl.h" 30 #include "src/v8.h" 31 #include "src/v8threads.h" 32 33 namespace v8 { 34 namespace internal { 35 36 37 const char* Marking::kWhiteBitPattern = "00"; 38 const char* Marking::kBlackBitPattern = "11"; 39 const char* Marking::kGreyBitPattern = "10"; 40 const char* Marking::kImpossibleBitPattern = "01"; 41 42 // The following has to hold in order for {ObjectMarking::MarkBitFrom} to not 43 // produce invalid {kImpossibleBitPattern} in the marking bitmap by overlapping. 44 STATIC_ASSERT(Heap::kMinObjectSizeInWords >= 2); 45 46 47 // ------------------------------------------------------------------------- 48 // MarkCompactCollector 49 50 MarkCompactCollector::MarkCompactCollector(Heap* heap) 51 : // NOLINT 52 heap_(heap), 53 page_parallel_job_semaphore_(0), 54 #ifdef DEBUG 55 state_(IDLE), 56 #endif 57 was_marked_incrementally_(false), 58 evacuation_(false), 59 compacting_(false), 60 black_allocation_(false), 61 have_code_to_deoptimize_(false), 62 marking_deque_(heap), 63 code_flusher_(nullptr), 64 sweeper_(heap) { 65 } 66 67 #ifdef VERIFY_HEAP 68 class VerifyMarkingVisitor : public ObjectVisitor { 69 public: 70 void VisitPointers(Object** start, Object** end) override { 71 for (Object** current = start; current < end; current++) { 72 if ((*current)->IsHeapObject()) { 73 HeapObject* object = HeapObject::cast(*current); 74 CHECK(ObjectMarking::IsBlackOrGrey(object)); 75 } 76 } 77 } 78 79 void VisitEmbeddedPointer(RelocInfo* rinfo) override { 80 DCHECK(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); 81 if (!rinfo->host()->IsWeakObject(rinfo->target_object())) { 82 Object* p = rinfo->target_object(); 83 VisitPointer(&p); 84 } 85 } 86 87 void VisitCell(RelocInfo* rinfo) override { 88 Code* code = rinfo->host(); 89 DCHECK(rinfo->rmode() == RelocInfo::CELL); 90 if (!code->IsWeakObject(rinfo->target_cell())) { 91 ObjectVisitor::VisitCell(rinfo); 92 } 93 } 94 }; 95 96 97 static void VerifyMarking(Heap* heap, Address bottom, Address top) { 98 VerifyMarkingVisitor visitor; 99 HeapObject* object; 100 Address next_object_must_be_here_or_later = bottom; 101 for (Address current = bottom; current < top;) { 102 object = HeapObject::FromAddress(current); 103 // One word fillers at the end of a black area can be grey. 104 if (ObjectMarking::IsBlackOrGrey(object) && 105 object->map() != heap->one_pointer_filler_map()) { 106 CHECK(ObjectMarking::IsBlack(object)); 107 CHECK(current >= next_object_must_be_here_or_later); 108 object->Iterate(&visitor); 109 next_object_must_be_here_or_later = current + object->Size(); 110 // The object is either part of a black area of black allocation or a 111 // regular black object 112 Page* page = Page::FromAddress(current); 113 CHECK( 114 page->markbits()->AllBitsSetInRange( 115 page->AddressToMarkbitIndex(current), 116 page->AddressToMarkbitIndex(next_object_must_be_here_or_later)) || 117 page->markbits()->AllBitsClearInRange( 118 page->AddressToMarkbitIndex(current + kPointerSize * 2), 119 page->AddressToMarkbitIndex(next_object_must_be_here_or_later))); 120 current = next_object_must_be_here_or_later; 121 } else { 122 current += kPointerSize; 123 } 124 } 125 } 126 127 static void VerifyMarking(NewSpace* space) { 128 Address end = space->top(); 129 // The bottom position is at the start of its page. Allows us to use 130 // page->area_start() as start of range on all pages. 131 CHECK_EQ(space->bottom(), Page::FromAddress(space->bottom())->area_start()); 132 133 PageRange range(space->bottom(), end); 134 for (auto it = range.begin(); it != range.end();) { 135 Page* page = *(it++); 136 Address limit = it != range.end() ? page->area_end() : end; 137 CHECK(limit == end || !page->Contains(end)); 138 VerifyMarking(space->heap(), page->area_start(), limit); 139 } 140 } 141 142 143 static void VerifyMarking(PagedSpace* space) { 144 for (Page* p : *space) { 145 VerifyMarking(space->heap(), p->area_start(), p->area_end()); 146 } 147 } 148 149 150 static void VerifyMarking(Heap* heap) { 151 VerifyMarking(heap->old_space()); 152 VerifyMarking(heap->code_space()); 153 VerifyMarking(heap->map_space()); 154 VerifyMarking(heap->new_space()); 155 156 VerifyMarkingVisitor visitor; 157 158 LargeObjectIterator it(heap->lo_space()); 159 for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { 160 if (ObjectMarking::IsBlackOrGrey(obj)) { 161 obj->Iterate(&visitor); 162 } 163 } 164 165 heap->IterateStrongRoots(&visitor, VISIT_ONLY_STRONG); 166 } 167 168 169 class VerifyEvacuationVisitor : public ObjectVisitor { 170 public: 171 void VisitPointers(Object** start, Object** end) override { 172 for (Object** current = start; current < end; current++) { 173 if ((*current)->IsHeapObject()) { 174 HeapObject* object = HeapObject::cast(*current); 175 CHECK(!MarkCompactCollector::IsOnEvacuationCandidate(object)); 176 } 177 } 178 } 179 }; 180 181 182 static void VerifyEvacuation(Page* page) { 183 VerifyEvacuationVisitor visitor; 184 HeapObjectIterator iterator(page); 185 for (HeapObject* heap_object = iterator.Next(); heap_object != NULL; 186 heap_object = iterator.Next()) { 187 // We skip free space objects. 188 if (!heap_object->IsFiller()) { 189 heap_object->Iterate(&visitor); 190 } 191 } 192 } 193 194 195 static void VerifyEvacuation(NewSpace* space) { 196 VerifyEvacuationVisitor visitor; 197 PageRange range(space->bottom(), space->top()); 198 for (auto it = range.begin(); it != range.end();) { 199 Page* page = *(it++); 200 Address current = page->area_start(); 201 Address limit = it != range.end() ? page->area_end() : space->top(); 202 CHECK(limit == space->top() || !page->Contains(space->top())); 203 while (current < limit) { 204 HeapObject* object = HeapObject::FromAddress(current); 205 object->Iterate(&visitor); 206 current += object->Size(); 207 } 208 } 209 } 210 211 212 static void VerifyEvacuation(Heap* heap, PagedSpace* space) { 213 if (FLAG_use_allocation_folding && (space == heap->old_space())) { 214 return; 215 } 216 for (Page* p : *space) { 217 if (p->IsEvacuationCandidate()) continue; 218 VerifyEvacuation(p); 219 } 220 } 221 222 223 static void VerifyEvacuation(Heap* heap) { 224 VerifyEvacuation(heap, heap->old_space()); 225 VerifyEvacuation(heap, heap->code_space()); 226 VerifyEvacuation(heap, heap->map_space()); 227 VerifyEvacuation(heap->new_space()); 228 229 VerifyEvacuationVisitor visitor; 230 heap->IterateStrongRoots(&visitor, VISIT_ALL); 231 } 232 #endif // VERIFY_HEAP 233 234 235 void MarkCompactCollector::SetUp() { 236 DCHECK(strcmp(Marking::kWhiteBitPattern, "00") == 0); 237 DCHECK(strcmp(Marking::kBlackBitPattern, "11") == 0); 238 DCHECK(strcmp(Marking::kGreyBitPattern, "10") == 0); 239 DCHECK(strcmp(Marking::kImpossibleBitPattern, "01") == 0); 240 marking_deque()->SetUp(); 241 242 if (FLAG_flush_code) { 243 code_flusher_ = new CodeFlusher(isolate()); 244 if (FLAG_trace_code_flushing) { 245 PrintF("[code-flushing is now on]\n"); 246 } 247 } 248 } 249 250 251 void MarkCompactCollector::TearDown() { 252 AbortCompaction(); 253 marking_deque()->TearDown(); 254 delete code_flusher_; 255 } 256 257 258 void MarkCompactCollector::AddEvacuationCandidate(Page* p) { 259 DCHECK(!p->NeverEvacuate()); 260 p->MarkEvacuationCandidate(); 261 evacuation_candidates_.Add(p); 262 } 263 264 265 static void TraceFragmentation(PagedSpace* space) { 266 int number_of_pages = space->CountTotalPages(); 267 intptr_t reserved = (number_of_pages * space->AreaSize()); 268 intptr_t free = reserved - space->SizeOfObjects(); 269 PrintF("[%s]: %d pages, %d (%.1f%%) free\n", 270 AllocationSpaceName(space->identity()), number_of_pages, 271 static_cast<int>(free), static_cast<double>(free) * 100 / reserved); 272 } 273 274 bool MarkCompactCollector::StartCompaction() { 275 if (!compacting_) { 276 DCHECK(evacuation_candidates_.length() == 0); 277 278 CollectEvacuationCandidates(heap()->old_space()); 279 280 if (FLAG_compact_code_space) { 281 CollectEvacuationCandidates(heap()->code_space()); 282 } else if (FLAG_trace_fragmentation) { 283 TraceFragmentation(heap()->code_space()); 284 } 285 286 if (FLAG_trace_fragmentation) { 287 TraceFragmentation(heap()->map_space()); 288 } 289 290 compacting_ = evacuation_candidates_.length() > 0; 291 } 292 293 return compacting_; 294 } 295 296 void MarkCompactCollector::CollectGarbage() { 297 // Make sure that Prepare() has been called. The individual steps below will 298 // update the state as they proceed. 299 DCHECK(state_ == PREPARE_GC); 300 301 MarkLiveObjects(); 302 303 DCHECK(heap_->incremental_marking()->IsStopped()); 304 305 ClearNonLiveReferences(); 306 307 RecordObjectStats(); 308 309 #ifdef VERIFY_HEAP 310 if (FLAG_verify_heap) { 311 VerifyMarking(heap_); 312 } 313 #endif 314 315 StartSweepSpaces(); 316 317 EvacuateNewSpaceAndCandidates(); 318 319 Finish(); 320 } 321 322 #ifdef VERIFY_HEAP 323 void MarkCompactCollector::VerifyMarkbitsAreClean(PagedSpace* space) { 324 for (Page* p : *space) { 325 CHECK(p->markbits()->IsClean()); 326 CHECK_EQ(0, p->LiveBytes()); 327 } 328 } 329 330 331 void MarkCompactCollector::VerifyMarkbitsAreClean(NewSpace* space) { 332 for (Page* p : PageRange(space->bottom(), space->top())) { 333 CHECK(p->markbits()->IsClean()); 334 CHECK_EQ(0, p->LiveBytes()); 335 } 336 } 337 338 339 void MarkCompactCollector::VerifyMarkbitsAreClean() { 340 VerifyMarkbitsAreClean(heap_->old_space()); 341 VerifyMarkbitsAreClean(heap_->code_space()); 342 VerifyMarkbitsAreClean(heap_->map_space()); 343 VerifyMarkbitsAreClean(heap_->new_space()); 344 345 LargeObjectIterator it(heap_->lo_space()); 346 for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { 347 CHECK(ObjectMarking::IsWhite(obj)); 348 CHECK_EQ(0, Page::FromAddress(obj->address())->LiveBytes()); 349 } 350 } 351 352 void MarkCompactCollector::VerifyWeakEmbeddedObjectsInCode() { 353 HeapObjectIterator code_iterator(heap()->code_space()); 354 for (HeapObject* obj = code_iterator.Next(); obj != NULL; 355 obj = code_iterator.Next()) { 356 Code* code = Code::cast(obj); 357 if (!code->is_optimized_code()) continue; 358 if (WillBeDeoptimized(code)) continue; 359 code->VerifyEmbeddedObjectsDependency(); 360 } 361 } 362 363 364 void MarkCompactCollector::VerifyOmittedMapChecks() { 365 HeapObjectIterator iterator(heap()->map_space()); 366 for (HeapObject* obj = iterator.Next(); obj != NULL; obj = iterator.Next()) { 367 Map* map = Map::cast(obj); 368 map->VerifyOmittedMapChecks(); 369 } 370 } 371 #endif // VERIFY_HEAP 372 373 374 static void ClearMarkbitsInPagedSpace(PagedSpace* space) { 375 for (Page* p : *space) { 376 p->ClearLiveness(); 377 } 378 } 379 380 381 static void ClearMarkbitsInNewSpace(NewSpace* space) { 382 for (Page* page : *space) { 383 page->ClearLiveness(); 384 } 385 } 386 387 388 void MarkCompactCollector::ClearMarkbits() { 389 ClearMarkbitsInPagedSpace(heap_->code_space()); 390 ClearMarkbitsInPagedSpace(heap_->map_space()); 391 ClearMarkbitsInPagedSpace(heap_->old_space()); 392 ClearMarkbitsInNewSpace(heap_->new_space()); 393 394 LargeObjectIterator it(heap_->lo_space()); 395 for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { 396 ObjectMarking::ClearMarkBit(obj); 397 MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address()); 398 chunk->ResetProgressBar(); 399 chunk->ResetLiveBytes(); 400 } 401 } 402 403 class MarkCompactCollector::Sweeper::SweeperTask : public v8::Task { 404 public: 405 SweeperTask(Sweeper* sweeper, base::Semaphore* pending_sweeper_tasks, 406 AllocationSpace space_to_start) 407 : sweeper_(sweeper), 408 pending_sweeper_tasks_(pending_sweeper_tasks), 409 space_to_start_(space_to_start) {} 410 411 virtual ~SweeperTask() {} 412 413 private: 414 // v8::Task overrides. 415 void Run() override { 416 DCHECK_GE(space_to_start_, FIRST_SPACE); 417 DCHECK_LE(space_to_start_, LAST_PAGED_SPACE); 418 const int offset = space_to_start_ - FIRST_SPACE; 419 const int num_spaces = LAST_PAGED_SPACE - FIRST_SPACE + 1; 420 for (int i = 0; i < num_spaces; i++) { 421 const int space_id = FIRST_SPACE + ((i + offset) % num_spaces); 422 DCHECK_GE(space_id, FIRST_SPACE); 423 DCHECK_LE(space_id, LAST_PAGED_SPACE); 424 sweeper_->ParallelSweepSpace(static_cast<AllocationSpace>(space_id), 0); 425 } 426 pending_sweeper_tasks_->Signal(); 427 } 428 429 Sweeper* sweeper_; 430 base::Semaphore* pending_sweeper_tasks_; 431 AllocationSpace space_to_start_; 432 433 DISALLOW_COPY_AND_ASSIGN(SweeperTask); 434 }; 435 436 void MarkCompactCollector::Sweeper::StartSweeping() { 437 sweeping_in_progress_ = true; 438 ForAllSweepingSpaces([this](AllocationSpace space) { 439 std::sort(sweeping_list_[space].begin(), sweeping_list_[space].end(), 440 [](Page* a, Page* b) { return a->LiveBytes() < b->LiveBytes(); }); 441 }); 442 } 443 444 void MarkCompactCollector::Sweeper::StartSweeperTasks() { 445 if (FLAG_concurrent_sweeping && sweeping_in_progress_) { 446 ForAllSweepingSpaces([this](AllocationSpace space) { 447 if (space == NEW_SPACE) return; 448 num_sweeping_tasks_.Increment(1); 449 V8::GetCurrentPlatform()->CallOnBackgroundThread( 450 new SweeperTask(this, &pending_sweeper_tasks_semaphore_, space), 451 v8::Platform::kShortRunningTask); 452 }); 453 } 454 } 455 456 void MarkCompactCollector::Sweeper::SweepOrWaitUntilSweepingCompleted( 457 Page* page) { 458 if (!page->SweepingDone()) { 459 ParallelSweepPage(page, page->owner()->identity()); 460 if (!page->SweepingDone()) { 461 // We were not able to sweep that page, i.e., a concurrent 462 // sweeper thread currently owns this page. Wait for the sweeper 463 // thread to be done with this page. 464 page->WaitUntilSweepingCompleted(); 465 } 466 } 467 } 468 469 void MarkCompactCollector::SweepAndRefill(CompactionSpace* space) { 470 if (FLAG_concurrent_sweeping && 471 !sweeper().IsSweepingCompleted(space->identity())) { 472 sweeper().ParallelSweepSpace(space->identity(), 0); 473 space->RefillFreeList(); 474 } 475 } 476 477 Page* MarkCompactCollector::Sweeper::GetSweptPageSafe(PagedSpace* space) { 478 base::LockGuard<base::Mutex> guard(&mutex_); 479 SweptList& list = swept_list_[space->identity()]; 480 if (list.length() > 0) { 481 return list.RemoveLast(); 482 } 483 return nullptr; 484 } 485 486 void MarkCompactCollector::Sweeper::EnsureCompleted() { 487 if (!sweeping_in_progress_) return; 488 489 // If sweeping is not completed or not running at all, we try to complete it 490 // here. 491 ForAllSweepingSpaces([this](AllocationSpace space) { 492 if (!FLAG_concurrent_sweeping || !this->IsSweepingCompleted(space)) { 493 ParallelSweepSpace(space, 0); 494 } 495 }); 496 497 if (FLAG_concurrent_sweeping) { 498 while (num_sweeping_tasks_.Value() > 0) { 499 pending_sweeper_tasks_semaphore_.Wait(); 500 num_sweeping_tasks_.Increment(-1); 501 } 502 } 503 504 ForAllSweepingSpaces([this](AllocationSpace space) { 505 if (space == NEW_SPACE) { 506 swept_list_[NEW_SPACE].Clear(); 507 } 508 DCHECK(sweeping_list_[space].empty()); 509 }); 510 sweeping_in_progress_ = false; 511 } 512 513 void MarkCompactCollector::Sweeper::EnsureNewSpaceCompleted() { 514 if (!sweeping_in_progress_) return; 515 if (!FLAG_concurrent_sweeping || !IsSweepingCompleted(NEW_SPACE)) { 516 for (Page* p : *heap_->new_space()) { 517 SweepOrWaitUntilSweepingCompleted(p); 518 } 519 } 520 } 521 522 void MarkCompactCollector::EnsureSweepingCompleted() { 523 if (!sweeper().sweeping_in_progress()) return; 524 525 sweeper().EnsureCompleted(); 526 heap()->old_space()->RefillFreeList(); 527 heap()->code_space()->RefillFreeList(); 528 heap()->map_space()->RefillFreeList(); 529 530 #ifdef VERIFY_HEAP 531 if (FLAG_verify_heap && !evacuation()) { 532 VerifyEvacuation(heap_); 533 } 534 #endif 535 } 536 537 bool MarkCompactCollector::Sweeper::AreSweeperTasksRunning() { 538 DCHECK(FLAG_concurrent_sweeping); 539 while (pending_sweeper_tasks_semaphore_.WaitFor( 540 base::TimeDelta::FromSeconds(0))) { 541 num_sweeping_tasks_.Increment(-1); 542 } 543 return num_sweeping_tasks_.Value() != 0; 544 } 545 546 bool MarkCompactCollector::Sweeper::IsSweepingCompleted(AllocationSpace space) { 547 DCHECK(FLAG_concurrent_sweeping); 548 if (AreSweeperTasksRunning()) return false; 549 base::LockGuard<base::Mutex> guard(&mutex_); 550 return sweeping_list_[space].empty(); 551 } 552 553 const char* AllocationSpaceName(AllocationSpace space) { 554 switch (space) { 555 case NEW_SPACE: 556 return "NEW_SPACE"; 557 case OLD_SPACE: 558 return "OLD_SPACE"; 559 case CODE_SPACE: 560 return "CODE_SPACE"; 561 case MAP_SPACE: 562 return "MAP_SPACE"; 563 case LO_SPACE: 564 return "LO_SPACE"; 565 default: 566 UNREACHABLE(); 567 } 568 569 return NULL; 570 } 571 572 void MarkCompactCollector::ComputeEvacuationHeuristics( 573 size_t area_size, int* target_fragmentation_percent, 574 size_t* max_evacuated_bytes) { 575 // For memory reducing and optimize for memory mode we directly define both 576 // constants. 577 const int kTargetFragmentationPercentForReduceMemory = 20; 578 const size_t kMaxEvacuatedBytesForReduceMemory = 12 * MB; 579 const int kTargetFragmentationPercentForOptimizeMemory = 20; 580 const size_t kMaxEvacuatedBytesForOptimizeMemory = 6 * MB; 581 582 // For regular mode (which is latency critical) we define less aggressive 583 // defaults to start and switch to a trace-based (using compaction speed) 584 // approach as soon as we have enough samples. 585 const int kTargetFragmentationPercent = 70; 586 const size_t kMaxEvacuatedBytes = 4 * MB; 587 // Time to take for a single area (=payload of page). Used as soon as there 588 // exist enough compaction speed samples. 589 const float kTargetMsPerArea = .5; 590 591 if (heap()->ShouldReduceMemory()) { 592 *target_fragmentation_percent = kTargetFragmentationPercentForReduceMemory; 593 *max_evacuated_bytes = kMaxEvacuatedBytesForReduceMemory; 594 } else if (heap()->ShouldOptimizeForMemoryUsage()) { 595 *target_fragmentation_percent = 596 kTargetFragmentationPercentForOptimizeMemory; 597 *max_evacuated_bytes = kMaxEvacuatedBytesForOptimizeMemory; 598 } else { 599 const double estimated_compaction_speed = 600 heap()->tracer()->CompactionSpeedInBytesPerMillisecond(); 601 if (estimated_compaction_speed != 0) { 602 // Estimate the target fragmentation based on traced compaction speed 603 // and a goal for a single page. 604 const double estimated_ms_per_area = 605 1 + area_size / estimated_compaction_speed; 606 *target_fragmentation_percent = static_cast<int>( 607 100 - 100 * kTargetMsPerArea / estimated_ms_per_area); 608 if (*target_fragmentation_percent < 609 kTargetFragmentationPercentForReduceMemory) { 610 *target_fragmentation_percent = 611 kTargetFragmentationPercentForReduceMemory; 612 } 613 } else { 614 *target_fragmentation_percent = kTargetFragmentationPercent; 615 } 616 *max_evacuated_bytes = kMaxEvacuatedBytes; 617 } 618 } 619 620 621 void MarkCompactCollector::CollectEvacuationCandidates(PagedSpace* space) { 622 DCHECK(space->identity() == OLD_SPACE || space->identity() == CODE_SPACE); 623 624 int number_of_pages = space->CountTotalPages(); 625 size_t area_size = space->AreaSize(); 626 627 // Pairs of (live_bytes_in_page, page). 628 typedef std::pair<size_t, Page*> LiveBytesPagePair; 629 std::vector<LiveBytesPagePair> pages; 630 pages.reserve(number_of_pages); 631 632 DCHECK(!sweeping_in_progress()); 633 DCHECK(!FLAG_concurrent_sweeping || 634 sweeper().IsSweepingCompleted(space->identity())); 635 Page* owner_of_linear_allocation_area = 636 space->top() == space->limit() 637 ? nullptr 638 : Page::FromAllocationAreaAddress(space->top()); 639 for (Page* p : *space) { 640 if (p->NeverEvacuate() || p == owner_of_linear_allocation_area) continue; 641 // Invariant: Evacuation candidates are just created when marking is 642 // started. This means that sweeping has finished. Furthermore, at the end 643 // of a GC all evacuation candidates are cleared and their slot buffers are 644 // released. 645 CHECK(!p->IsEvacuationCandidate()); 646 CHECK_NULL(p->old_to_old_slots()); 647 CHECK_NULL(p->typed_old_to_old_slots()); 648 CHECK(p->SweepingDone()); 649 DCHECK(p->area_size() == area_size); 650 pages.push_back(std::make_pair(p->LiveBytesFromFreeList(), p)); 651 } 652 653 int candidate_count = 0; 654 size_t total_live_bytes = 0; 655 656 const bool reduce_memory = heap()->ShouldReduceMemory(); 657 if (FLAG_manual_evacuation_candidates_selection) { 658 for (size_t i = 0; i < pages.size(); i++) { 659 Page* p = pages[i].second; 660 if (p->IsFlagSet(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING)) { 661 candidate_count++; 662 total_live_bytes += pages[i].first; 663 p->ClearFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING); 664 AddEvacuationCandidate(p); 665 } 666 } 667 } else if (FLAG_stress_compaction) { 668 for (size_t i = 0; i < pages.size(); i++) { 669 Page* p = pages[i].second; 670 if (i % 2 == 0) { 671 candidate_count++; 672 total_live_bytes += pages[i].first; 673 AddEvacuationCandidate(p); 674 } 675 } 676 } else { 677 // The following approach determines the pages that should be evacuated. 678 // 679 // We use two conditions to decide whether a page qualifies as an evacuation 680 // candidate, or not: 681 // * Target fragmentation: How fragmented is a page, i.e., how is the ratio 682 // between live bytes and capacity of this page (= area). 683 // * Evacuation quota: A global quota determining how much bytes should be 684 // compacted. 685 // 686 // The algorithm sorts all pages by live bytes and then iterates through 687 // them starting with the page with the most free memory, adding them to the 688 // set of evacuation candidates as long as both conditions (fragmentation 689 // and quota) hold. 690 size_t max_evacuated_bytes; 691 int target_fragmentation_percent; 692 ComputeEvacuationHeuristics(area_size, &target_fragmentation_percent, 693 &max_evacuated_bytes); 694 695 const size_t free_bytes_threshold = 696 target_fragmentation_percent * (area_size / 100); 697 698 // Sort pages from the most free to the least free, then select 699 // the first n pages for evacuation such that: 700 // - the total size of evacuated objects does not exceed the specified 701 // limit. 702 // - fragmentation of (n+1)-th page does not exceed the specified limit. 703 std::sort(pages.begin(), pages.end(), 704 [](const LiveBytesPagePair& a, const LiveBytesPagePair& b) { 705 return a.first < b.first; 706 }); 707 for (size_t i = 0; i < pages.size(); i++) { 708 size_t live_bytes = pages[i].first; 709 DCHECK_GE(area_size, live_bytes); 710 size_t free_bytes = area_size - live_bytes; 711 if (FLAG_always_compact || 712 ((free_bytes >= free_bytes_threshold) && 713 ((total_live_bytes + live_bytes) <= max_evacuated_bytes))) { 714 candidate_count++; 715 total_live_bytes += live_bytes; 716 } 717 if (FLAG_trace_fragmentation_verbose) { 718 PrintIsolate(isolate(), 719 "compaction-selection-page: space=%s free_bytes_page=%zu " 720 "fragmentation_limit_kb=%" PRIuS 721 " fragmentation_limit_percent=%d sum_compaction_kb=%zu " 722 "compaction_limit_kb=%zu\n", 723 AllocationSpaceName(space->identity()), free_bytes / KB, 724 free_bytes_threshold / KB, target_fragmentation_percent, 725 total_live_bytes / KB, max_evacuated_bytes / KB); 726 } 727 } 728 // How many pages we will allocated for the evacuated objects 729 // in the worst case: ceil(total_live_bytes / area_size) 730 int estimated_new_pages = 731 static_cast<int>((total_live_bytes + area_size - 1) / area_size); 732 DCHECK_LE(estimated_new_pages, candidate_count); 733 int estimated_released_pages = candidate_count - estimated_new_pages; 734 // Avoid (compact -> expand) cycles. 735 if ((estimated_released_pages == 0) && !FLAG_always_compact) { 736 candidate_count = 0; 737 } 738 for (int i = 0; i < candidate_count; i++) { 739 AddEvacuationCandidate(pages[i].second); 740 } 741 } 742 743 if (FLAG_trace_fragmentation) { 744 PrintIsolate(isolate(), 745 "compaction-selection: space=%s reduce_memory=%d pages=%d " 746 "total_live_bytes=%zu\n", 747 AllocationSpaceName(space->identity()), reduce_memory, 748 candidate_count, total_live_bytes / KB); 749 } 750 } 751 752 753 void MarkCompactCollector::AbortCompaction() { 754 if (compacting_) { 755 RememberedSet<OLD_TO_OLD>::ClearAll(heap()); 756 for (Page* p : evacuation_candidates_) { 757 p->ClearEvacuationCandidate(); 758 } 759 compacting_ = false; 760 evacuation_candidates_.Rewind(0); 761 } 762 DCHECK_EQ(0, evacuation_candidates_.length()); 763 } 764 765 766 void MarkCompactCollector::Prepare() { 767 was_marked_incrementally_ = heap()->incremental_marking()->IsMarking(); 768 769 #ifdef DEBUG 770 DCHECK(state_ == IDLE); 771 state_ = PREPARE_GC; 772 #endif 773 774 DCHECK(!FLAG_never_compact || !FLAG_always_compact); 775 776 // Instead of waiting we could also abort the sweeper threads here. 777 EnsureSweepingCompleted(); 778 779 if (heap()->incremental_marking()->IsSweeping()) { 780 heap()->incremental_marking()->Stop(); 781 } 782 783 // If concurrent unmapping tasks are still running, we should wait for 784 // them here. 785 heap()->memory_allocator()->unmapper()->WaitUntilCompleted(); 786 787 // Clear marking bits if incremental marking is aborted. 788 if (was_marked_incrementally_ && heap_->ShouldAbortIncrementalMarking()) { 789 heap()->incremental_marking()->Stop(); 790 heap()->incremental_marking()->AbortBlackAllocation(); 791 ClearMarkbits(); 792 AbortWeakCollections(); 793 AbortWeakCells(); 794 AbortTransitionArrays(); 795 AbortCompaction(); 796 heap_->local_embedder_heap_tracer()->AbortTracing(); 797 marking_deque()->Clear(); 798 was_marked_incrementally_ = false; 799 } 800 801 if (!was_marked_incrementally_) { 802 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WRAPPER_PROLOGUE); 803 heap_->local_embedder_heap_tracer()->TracePrologue(); 804 } 805 806 // Don't start compaction if we are in the middle of incremental 807 // marking cycle. We did not collect any slots. 808 if (!FLAG_never_compact && !was_marked_incrementally_) { 809 StartCompaction(); 810 } 811 812 PagedSpaces spaces(heap()); 813 for (PagedSpace* space = spaces.next(); space != NULL; 814 space = spaces.next()) { 815 space->PrepareForMarkCompact(); 816 } 817 heap()->account_external_memory_concurrently_freed(); 818 819 #ifdef VERIFY_HEAP 820 if (!was_marked_incrementally_ && FLAG_verify_heap) { 821 VerifyMarkbitsAreClean(); 822 } 823 #endif 824 } 825 826 827 void MarkCompactCollector::Finish() { 828 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_FINISH); 829 830 if (!heap()->delay_sweeper_tasks_for_testing_) { 831 sweeper().StartSweeperTasks(); 832 } 833 834 // The hashing of weak_object_to_code_table is no longer valid. 835 heap()->weak_object_to_code_table()->Rehash( 836 heap()->isolate()->factory()->undefined_value()); 837 838 // Clear the marking state of live large objects. 839 heap_->lo_space()->ClearMarkingStateOfLiveObjects(); 840 841 #ifdef DEBUG 842 DCHECK(state_ == SWEEP_SPACES || state_ == RELOCATE_OBJECTS); 843 state_ = IDLE; 844 #endif 845 heap_->isolate()->inner_pointer_to_code_cache()->Flush(); 846 847 // The stub caches are not traversed during GC; clear them to force 848 // their lazy re-initialization. This must be done after the 849 // GC, because it relies on the new address of certain old space 850 // objects (empty string, illegal builtin). 851 isolate()->load_stub_cache()->Clear(); 852 isolate()->store_stub_cache()->Clear(); 853 854 if (have_code_to_deoptimize_) { 855 // Some code objects were marked for deoptimization during the GC. 856 Deoptimizer::DeoptimizeMarkedCode(isolate()); 857 have_code_to_deoptimize_ = false; 858 } 859 860 heap_->incremental_marking()->ClearIdleMarkingDelayCounter(); 861 } 862 863 864 // ------------------------------------------------------------------------- 865 // Phase 1: tracing and marking live objects. 866 // before: all objects are in normal state. 867 // after: a live object's map pointer is marked as '00'. 868 869 // Marking all live objects in the heap as part of mark-sweep or mark-compact 870 // collection. Before marking, all objects are in their normal state. After 871 // marking, live objects' map pointers are marked indicating that the object 872 // has been found reachable. 873 // 874 // The marking algorithm is a (mostly) depth-first (because of possible stack 875 // overflow) traversal of the graph of objects reachable from the roots. It 876 // uses an explicit stack of pointers rather than recursion. The young 877 // generation's inactive ('from') space is used as a marking stack. The 878 // objects in the marking stack are the ones that have been reached and marked 879 // but their children have not yet been visited. 880 // 881 // The marking stack can overflow during traversal. In that case, we set an 882 // overflow flag. When the overflow flag is set, we continue marking objects 883 // reachable from the objects on the marking stack, but no longer push them on 884 // the marking stack. Instead, we mark them as both marked and overflowed. 885 // When the stack is in the overflowed state, objects marked as overflowed 886 // have been reached and marked but their children have not been visited yet. 887 // After emptying the marking stack, we clear the overflow flag and traverse 888 // the heap looking for objects marked as overflowed, push them on the stack, 889 // and continue with marking. This process repeats until all reachable 890 // objects have been marked. 891 892 void CodeFlusher::ProcessJSFunctionCandidates() { 893 Code* lazy_compile = isolate_->builtins()->builtin(Builtins::kCompileLazy); 894 Code* interpreter_entry_trampoline = 895 isolate_->builtins()->builtin(Builtins::kInterpreterEntryTrampoline); 896 Object* undefined = isolate_->heap()->undefined_value(); 897 898 JSFunction* candidate = jsfunction_candidates_head_; 899 JSFunction* next_candidate; 900 while (candidate != NULL) { 901 next_candidate = GetNextCandidate(candidate); 902 ClearNextCandidate(candidate, undefined); 903 904 SharedFunctionInfo* shared = candidate->shared(); 905 906 Code* code = shared->code(); 907 if (ObjectMarking::IsWhite(code)) { 908 if (FLAG_trace_code_flushing && shared->is_compiled()) { 909 PrintF("[code-flushing clears: "); 910 shared->ShortPrint(); 911 PrintF(" - age: %d]\n", code->GetAge()); 912 } 913 // Always flush the optimized code map if there is one. 914 if (!shared->OptimizedCodeMapIsCleared()) { 915 shared->ClearOptimizedCodeMap(); 916 } 917 if (shared->HasBytecodeArray()) { 918 shared->set_code(interpreter_entry_trampoline); 919 candidate->set_code(interpreter_entry_trampoline); 920 } else { 921 shared->set_code(lazy_compile); 922 candidate->set_code(lazy_compile); 923 } 924 } else { 925 DCHECK(ObjectMarking::IsBlack(code)); 926 candidate->set_code(code); 927 } 928 929 // We are in the middle of a GC cycle so the write barrier in the code 930 // setter did not record the slot update and we have to do that manually. 931 Address slot = candidate->address() + JSFunction::kCodeEntryOffset; 932 Code* target = Code::cast(Code::GetObjectFromEntryAddress(slot)); 933 isolate_->heap()->mark_compact_collector()->RecordCodeEntrySlot( 934 candidate, slot, target); 935 936 Object** shared_code_slot = 937 HeapObject::RawField(shared, SharedFunctionInfo::kCodeOffset); 938 isolate_->heap()->mark_compact_collector()->RecordSlot( 939 shared, shared_code_slot, *shared_code_slot); 940 941 candidate = next_candidate; 942 } 943 944 jsfunction_candidates_head_ = NULL; 945 } 946 947 948 void CodeFlusher::ProcessSharedFunctionInfoCandidates() { 949 Code* lazy_compile = isolate_->builtins()->builtin(Builtins::kCompileLazy); 950 Code* interpreter_entry_trampoline = 951 isolate_->builtins()->builtin(Builtins::kInterpreterEntryTrampoline); 952 SharedFunctionInfo* candidate = shared_function_info_candidates_head_; 953 SharedFunctionInfo* next_candidate; 954 while (candidate != NULL) { 955 next_candidate = GetNextCandidate(candidate); 956 ClearNextCandidate(candidate); 957 958 Code* code = candidate->code(); 959 if (ObjectMarking::IsWhite(code)) { 960 if (FLAG_trace_code_flushing && candidate->is_compiled()) { 961 PrintF("[code-flushing clears: "); 962 candidate->ShortPrint(); 963 PrintF(" - age: %d]\n", code->GetAge()); 964 } 965 // Always flush the optimized code map if there is one. 966 if (!candidate->OptimizedCodeMapIsCleared()) { 967 candidate->ClearOptimizedCodeMap(); 968 } 969 if (candidate->HasBytecodeArray()) { 970 candidate->set_code(interpreter_entry_trampoline); 971 } else { 972 candidate->set_code(lazy_compile); 973 } 974 } 975 976 Object** code_slot = 977 HeapObject::RawField(candidate, SharedFunctionInfo::kCodeOffset); 978 isolate_->heap()->mark_compact_collector()->RecordSlot(candidate, code_slot, 979 *code_slot); 980 981 candidate = next_candidate; 982 } 983 984 shared_function_info_candidates_head_ = NULL; 985 } 986 987 988 void CodeFlusher::EvictCandidate(SharedFunctionInfo* shared_info) { 989 // Make sure previous flushing decisions are revisited. 990 isolate_->heap()->incremental_marking()->IterateBlackObject(shared_info); 991 992 if (FLAG_trace_code_flushing) { 993 PrintF("[code-flushing abandons function-info: "); 994 shared_info->ShortPrint(); 995 PrintF("]\n"); 996 } 997 998 SharedFunctionInfo* candidate = shared_function_info_candidates_head_; 999 SharedFunctionInfo* next_candidate; 1000 if (candidate == shared_info) { 1001 next_candidate = GetNextCandidate(shared_info); 1002 shared_function_info_candidates_head_ = next_candidate; 1003 ClearNextCandidate(shared_info); 1004 } else { 1005 while (candidate != NULL) { 1006 next_candidate = GetNextCandidate(candidate); 1007 1008 if (next_candidate == shared_info) { 1009 next_candidate = GetNextCandidate(shared_info); 1010 SetNextCandidate(candidate, next_candidate); 1011 ClearNextCandidate(shared_info); 1012 break; 1013 } 1014 1015 candidate = next_candidate; 1016 } 1017 } 1018 } 1019 1020 1021 void CodeFlusher::EvictCandidate(JSFunction* function) { 1022 DCHECK(!function->next_function_link()->IsUndefined(isolate_)); 1023 Object* undefined = isolate_->heap()->undefined_value(); 1024 1025 // Make sure previous flushing decisions are revisited. 1026 isolate_->heap()->incremental_marking()->IterateBlackObject(function); 1027 isolate_->heap()->incremental_marking()->IterateBlackObject( 1028 function->shared()); 1029 1030 if (FLAG_trace_code_flushing) { 1031 PrintF("[code-flushing abandons closure: "); 1032 function->shared()->ShortPrint(); 1033 PrintF("]\n"); 1034 } 1035 1036 JSFunction* candidate = jsfunction_candidates_head_; 1037 JSFunction* next_candidate; 1038 if (candidate == function) { 1039 next_candidate = GetNextCandidate(function); 1040 jsfunction_candidates_head_ = next_candidate; 1041 ClearNextCandidate(function, undefined); 1042 } else { 1043 while (candidate != NULL) { 1044 next_candidate = GetNextCandidate(candidate); 1045 1046 if (next_candidate == function) { 1047 next_candidate = GetNextCandidate(function); 1048 SetNextCandidate(candidate, next_candidate); 1049 ClearNextCandidate(function, undefined); 1050 break; 1051 } 1052 1053 candidate = next_candidate; 1054 } 1055 } 1056 } 1057 1058 1059 void CodeFlusher::IteratePointersToFromSpace(ObjectVisitor* v) { 1060 Heap* heap = isolate_->heap(); 1061 1062 JSFunction** slot = &jsfunction_candidates_head_; 1063 JSFunction* candidate = jsfunction_candidates_head_; 1064 while (candidate != NULL) { 1065 if (heap->InFromSpace(candidate)) { 1066 v->VisitPointer(reinterpret_cast<Object**>(slot)); 1067 } 1068 candidate = GetNextCandidate(*slot); 1069 slot = GetNextCandidateSlot(*slot); 1070 } 1071 } 1072 1073 class StaticYoungGenerationMarkingVisitor 1074 : public StaticNewSpaceVisitor<StaticYoungGenerationMarkingVisitor> { 1075 public: 1076 static void Initialize(Heap* heap) { 1077 StaticNewSpaceVisitor<StaticYoungGenerationMarkingVisitor>::Initialize(); 1078 } 1079 1080 inline static void VisitPointer(Heap* heap, HeapObject* object, Object** p) { 1081 Object* target = *p; 1082 if (heap->InNewSpace(target)) { 1083 if (MarkRecursively(heap, HeapObject::cast(target))) return; 1084 heap->mark_compact_collector()->MarkObject(HeapObject::cast(target)); 1085 } 1086 } 1087 1088 protected: 1089 inline static bool MarkRecursively(Heap* heap, HeapObject* object) { 1090 StackLimitCheck check(heap->isolate()); 1091 if (check.HasOverflowed()) return false; 1092 1093 if (ObjectMarking::IsBlackOrGrey(object)) return true; 1094 ObjectMarking::WhiteToBlack(object); 1095 IterateBody(object->map(), object); 1096 return true; 1097 } 1098 }; 1099 1100 class MarkCompactMarkingVisitor 1101 : public StaticMarkingVisitor<MarkCompactMarkingVisitor> { 1102 public: 1103 static void Initialize(); 1104 1105 INLINE(static void VisitPointer(Heap* heap, HeapObject* object, Object** p)) { 1106 MarkObjectByPointer(heap->mark_compact_collector(), object, p); 1107 } 1108 1109 INLINE(static void VisitPointers(Heap* heap, HeapObject* object, 1110 Object** start, Object** end)) { 1111 // Mark all objects pointed to in [start, end). 1112 const int kMinRangeForMarkingRecursion = 64; 1113 if (end - start >= kMinRangeForMarkingRecursion) { 1114 if (VisitUnmarkedObjects(heap, object, start, end)) return; 1115 // We are close to a stack overflow, so just mark the objects. 1116 } 1117 MarkCompactCollector* collector = heap->mark_compact_collector(); 1118 for (Object** p = start; p < end; p++) { 1119 MarkObjectByPointer(collector, object, p); 1120 } 1121 } 1122 1123 // Marks the object black and pushes it on the marking stack. 1124 INLINE(static void MarkObject(Heap* heap, HeapObject* object)) { 1125 heap->mark_compact_collector()->MarkObject(object); 1126 } 1127 1128 // Marks the object black without pushing it on the marking stack. 1129 // Returns true if object needed marking and false otherwise. 1130 INLINE(static bool MarkObjectWithoutPush(Heap* heap, HeapObject* object)) { 1131 if (ObjectMarking::IsWhite(object)) { 1132 ObjectMarking::WhiteToBlack(object); 1133 return true; 1134 } 1135 return false; 1136 } 1137 1138 // Mark object pointed to by p. 1139 INLINE(static void MarkObjectByPointer(MarkCompactCollector* collector, 1140 HeapObject* object, Object** p)) { 1141 if (!(*p)->IsHeapObject()) return; 1142 HeapObject* target_object = HeapObject::cast(*p); 1143 collector->RecordSlot(object, p, target_object); 1144 collector->MarkObject(target_object); 1145 } 1146 1147 1148 // Visit an unmarked object. 1149 INLINE(static void VisitUnmarkedObject(MarkCompactCollector* collector, 1150 HeapObject* obj)) { 1151 #ifdef DEBUG 1152 DCHECK(collector->heap()->Contains(obj)); 1153 DCHECK(ObjectMarking::IsWhite(obj)); 1154 #endif 1155 Map* map = obj->map(); 1156 Heap* heap = obj->GetHeap(); 1157 ObjectMarking::WhiteToBlack(obj); 1158 // Mark the map pointer and the body. 1159 heap->mark_compact_collector()->MarkObject(map); 1160 IterateBody(map, obj); 1161 } 1162 1163 // Visit all unmarked objects pointed to by [start, end). 1164 // Returns false if the operation fails (lack of stack space). 1165 INLINE(static bool VisitUnmarkedObjects(Heap* heap, HeapObject* object, 1166 Object** start, Object** end)) { 1167 // Return false is we are close to the stack limit. 1168 StackLimitCheck check(heap->isolate()); 1169 if (check.HasOverflowed()) return false; 1170 1171 MarkCompactCollector* collector = heap->mark_compact_collector(); 1172 // Visit the unmarked objects. 1173 for (Object** p = start; p < end; p++) { 1174 Object* o = *p; 1175 if (!o->IsHeapObject()) continue; 1176 collector->RecordSlot(object, p, o); 1177 HeapObject* obj = HeapObject::cast(o); 1178 if (ObjectMarking::IsBlackOrGrey(obj)) continue; 1179 VisitUnmarkedObject(collector, obj); 1180 } 1181 return true; 1182 } 1183 1184 private: 1185 // Code flushing support. 1186 1187 static const int kRegExpCodeThreshold = 5; 1188 1189 static void UpdateRegExpCodeAgeAndFlush(Heap* heap, JSRegExp* re, 1190 bool is_one_byte) { 1191 // Make sure that the fixed array is in fact initialized on the RegExp. 1192 // We could potentially trigger a GC when initializing the RegExp. 1193 if (HeapObject::cast(re->data())->map()->instance_type() != 1194 FIXED_ARRAY_TYPE) 1195 return; 1196 1197 // Make sure this is a RegExp that actually contains code. 1198 if (re->TypeTag() != JSRegExp::IRREGEXP) return; 1199 1200 Object* code = re->DataAt(JSRegExp::code_index(is_one_byte)); 1201 if (!code->IsSmi() && 1202 HeapObject::cast(code)->map()->instance_type() == CODE_TYPE) { 1203 // Save a copy that can be reinstated if we need the code again. 1204 re->SetDataAt(JSRegExp::saved_code_index(is_one_byte), code); 1205 1206 // Saving a copy might create a pointer into compaction candidate 1207 // that was not observed by marker. This might happen if JSRegExp data 1208 // was marked through the compilation cache before marker reached JSRegExp 1209 // object. 1210 FixedArray* data = FixedArray::cast(re->data()); 1211 if (ObjectMarking::IsBlackOrGrey(data)) { 1212 Object** slot = 1213 data->data_start() + JSRegExp::saved_code_index(is_one_byte); 1214 heap->mark_compact_collector()->RecordSlot(data, slot, code); 1215 } 1216 1217 // Set a number in the 0-255 range to guarantee no smi overflow. 1218 re->SetDataAt(JSRegExp::code_index(is_one_byte), 1219 Smi::FromInt(heap->ms_count() & 0xff)); 1220 } else if (code->IsSmi()) { 1221 int value = Smi::cast(code)->value(); 1222 // The regexp has not been compiled yet or there was a compilation error. 1223 if (value == JSRegExp::kUninitializedValue || 1224 value == JSRegExp::kCompilationErrorValue) { 1225 return; 1226 } 1227 1228 // Check if we should flush now. 1229 if (value == ((heap->ms_count() - kRegExpCodeThreshold) & 0xff)) { 1230 re->SetDataAt(JSRegExp::code_index(is_one_byte), 1231 Smi::FromInt(JSRegExp::kUninitializedValue)); 1232 re->SetDataAt(JSRegExp::saved_code_index(is_one_byte), 1233 Smi::FromInt(JSRegExp::kUninitializedValue)); 1234 } 1235 } 1236 } 1237 1238 1239 // Works by setting the current sweep_generation (as a smi) in the 1240 // code object place in the data array of the RegExp and keeps a copy 1241 // around that can be reinstated if we reuse the RegExp before flushing. 1242 // If we did not use the code for kRegExpCodeThreshold mark sweep GCs 1243 // we flush the code. 1244 static void VisitRegExpAndFlushCode(Map* map, HeapObject* object) { 1245 Heap* heap = map->GetHeap(); 1246 MarkCompactCollector* collector = heap->mark_compact_collector(); 1247 if (!collector->is_code_flushing_enabled()) { 1248 JSObjectVisitor::Visit(map, object); 1249 return; 1250 } 1251 JSRegExp* re = reinterpret_cast<JSRegExp*>(object); 1252 // Flush code or set age on both one byte and two byte code. 1253 UpdateRegExpCodeAgeAndFlush(heap, re, true); 1254 UpdateRegExpCodeAgeAndFlush(heap, re, false); 1255 // Visit the fields of the RegExp, including the updated FixedArray. 1256 JSObjectVisitor::Visit(map, object); 1257 } 1258 }; 1259 1260 1261 void MarkCompactMarkingVisitor::Initialize() { 1262 StaticMarkingVisitor<MarkCompactMarkingVisitor>::Initialize(); 1263 1264 table_.Register(kVisitJSRegExp, &VisitRegExpAndFlushCode); 1265 } 1266 1267 1268 class CodeMarkingVisitor : public ThreadVisitor { 1269 public: 1270 explicit CodeMarkingVisitor(MarkCompactCollector* collector) 1271 : collector_(collector) {} 1272 1273 void VisitThread(Isolate* isolate, ThreadLocalTop* top) { 1274 collector_->PrepareThreadForCodeFlushing(isolate, top); 1275 } 1276 1277 private: 1278 MarkCompactCollector* collector_; 1279 }; 1280 1281 1282 class SharedFunctionInfoMarkingVisitor : public ObjectVisitor { 1283 public: 1284 explicit SharedFunctionInfoMarkingVisitor(MarkCompactCollector* collector) 1285 : collector_(collector) {} 1286 1287 void VisitPointers(Object** start, Object** end) override { 1288 for (Object** p = start; p < end; p++) VisitPointer(p); 1289 } 1290 1291 void VisitPointer(Object** slot) override { 1292 Object* obj = *slot; 1293 if (obj->IsSharedFunctionInfo()) { 1294 SharedFunctionInfo* shared = reinterpret_cast<SharedFunctionInfo*>(obj); 1295 collector_->MarkObject(shared->code()); 1296 collector_->MarkObject(shared); 1297 } 1298 } 1299 1300 private: 1301 MarkCompactCollector* collector_; 1302 }; 1303 1304 1305 void MarkCompactCollector::PrepareThreadForCodeFlushing(Isolate* isolate, 1306 ThreadLocalTop* top) { 1307 for (StackFrameIterator it(isolate, top); !it.done(); it.Advance()) { 1308 // Note: for the frame that has a pending lazy deoptimization 1309 // StackFrame::unchecked_code will return a non-optimized code object for 1310 // the outermost function and StackFrame::LookupCode will return 1311 // actual optimized code object. 1312 StackFrame* frame = it.frame(); 1313 Code* code = frame->unchecked_code(); 1314 MarkObject(code); 1315 if (frame->is_optimized()) { 1316 Code* optimized_code = frame->LookupCode(); 1317 MarkObject(optimized_code); 1318 } 1319 } 1320 } 1321 1322 1323 void MarkCompactCollector::PrepareForCodeFlushing() { 1324 // If code flushing is disabled, there is no need to prepare for it. 1325 if (!is_code_flushing_enabled()) return; 1326 1327 // Make sure we are not referencing the code from the stack. 1328 DCHECK(this == heap()->mark_compact_collector()); 1329 PrepareThreadForCodeFlushing(heap()->isolate(), 1330 heap()->isolate()->thread_local_top()); 1331 1332 // Iterate the archived stacks in all threads to check if 1333 // the code is referenced. 1334 CodeMarkingVisitor code_marking_visitor(this); 1335 heap()->isolate()->thread_manager()->IterateArchivedThreads( 1336 &code_marking_visitor); 1337 1338 SharedFunctionInfoMarkingVisitor visitor(this); 1339 heap()->isolate()->compilation_cache()->IterateFunctions(&visitor); 1340 heap()->isolate()->handle_scope_implementer()->Iterate(&visitor); 1341 1342 ProcessMarkingDeque<MarkCompactMode::FULL>(); 1343 } 1344 1345 1346 // Visitor class for marking heap roots. 1347 template <MarkCompactMode mode> 1348 class RootMarkingVisitor : public ObjectVisitor { 1349 public: 1350 explicit RootMarkingVisitor(Heap* heap) 1351 : collector_(heap->mark_compact_collector()) {} 1352 1353 void VisitPointer(Object** p) override { MarkObjectByPointer(p); } 1354 1355 void VisitPointers(Object** start, Object** end) override { 1356 for (Object** p = start; p < end; p++) MarkObjectByPointer(p); 1357 } 1358 1359 // Skip the weak next code link in a code object, which is visited in 1360 // ProcessTopOptimizedFrame. 1361 void VisitNextCodeLink(Object** p) override {} 1362 1363 private: 1364 void MarkObjectByPointer(Object** p) { 1365 if (!(*p)->IsHeapObject()) return; 1366 1367 HeapObject* object = HeapObject::cast(*p); 1368 1369 if (mode == MarkCompactMode::YOUNG_GENERATION && 1370 !collector_->heap()->InNewSpace(object)) 1371 return; 1372 1373 if (ObjectMarking::IsBlackOrGrey(object)) return; 1374 1375 Map* map = object->map(); 1376 // Mark the object. 1377 ObjectMarking::WhiteToBlack(object); 1378 1379 switch (mode) { 1380 case MarkCompactMode::FULL: { 1381 // Mark the map pointer and body, and push them on the marking stack. 1382 collector_->MarkObject(map); 1383 MarkCompactMarkingVisitor::IterateBody(map, object); 1384 } break; 1385 case MarkCompactMode::YOUNG_GENERATION: 1386 StaticYoungGenerationMarkingVisitor::IterateBody(map, object); 1387 break; 1388 } 1389 1390 // Mark all the objects reachable from the map and body. May leave 1391 // overflowed objects in the heap. 1392 collector_->EmptyMarkingDeque<mode>(); 1393 } 1394 1395 MarkCompactCollector* collector_; 1396 }; 1397 1398 1399 // Helper class for pruning the string table. 1400 template <bool finalize_external_strings, bool record_slots> 1401 class StringTableCleaner : public ObjectVisitor { 1402 public: 1403 StringTableCleaner(Heap* heap, HeapObject* table) 1404 : heap_(heap), pointers_removed_(0), table_(table) { 1405 DCHECK(!record_slots || table != nullptr); 1406 } 1407 1408 void VisitPointers(Object** start, Object** end) override { 1409 // Visit all HeapObject pointers in [start, end). 1410 MarkCompactCollector* collector = heap_->mark_compact_collector(); 1411 for (Object** p = start; p < end; p++) { 1412 Object* o = *p; 1413 if (o->IsHeapObject()) { 1414 if (ObjectMarking::IsWhite(HeapObject::cast(o))) { 1415 if (finalize_external_strings) { 1416 if (o->IsExternalString()) { 1417 heap_->FinalizeExternalString(String::cast(*p)); 1418 } else { 1419 // The original external string may have been internalized. 1420 DCHECK(o->IsThinString()); 1421 } 1422 } else { 1423 pointers_removed_++; 1424 } 1425 // Set the entry to the_hole_value (as deleted). 1426 *p = heap_->the_hole_value(); 1427 } else if (record_slots) { 1428 // StringTable contains only old space strings. 1429 DCHECK(!heap_->InNewSpace(o)); 1430 collector->RecordSlot(table_, p, o); 1431 } 1432 } 1433 } 1434 } 1435 1436 int PointersRemoved() { 1437 DCHECK(!finalize_external_strings); 1438 return pointers_removed_; 1439 } 1440 1441 private: 1442 Heap* heap_; 1443 int pointers_removed_; 1444 HeapObject* table_; 1445 }; 1446 1447 typedef StringTableCleaner<false, true> InternalizedStringTableCleaner; 1448 typedef StringTableCleaner<true, false> ExternalStringTableCleaner; 1449 1450 // Implementation of WeakObjectRetainer for mark compact GCs. All marked objects 1451 // are retained. 1452 class MarkCompactWeakObjectRetainer : public WeakObjectRetainer { 1453 public: 1454 virtual Object* RetainAs(Object* object) { 1455 DCHECK(!ObjectMarking::IsGrey(HeapObject::cast(object))); 1456 if (ObjectMarking::IsBlack(HeapObject::cast(object))) { 1457 return object; 1458 } else if (object->IsAllocationSite() && 1459 !(AllocationSite::cast(object)->IsZombie())) { 1460 // "dead" AllocationSites need to live long enough for a traversal of new 1461 // space. These sites get a one-time reprieve. 1462 AllocationSite* site = AllocationSite::cast(object); 1463 site->MarkZombie(); 1464 ObjectMarking::WhiteToBlack(site); 1465 return object; 1466 } else { 1467 return NULL; 1468 } 1469 } 1470 }; 1471 1472 1473 // Fill the marking stack with overflowed objects returned by the given 1474 // iterator. Stop when the marking stack is filled or the end of the space 1475 // is reached, whichever comes first. 1476 template <class T> 1477 void MarkCompactCollector::DiscoverGreyObjectsWithIterator(T* it) { 1478 // The caller should ensure that the marking stack is initially not full, 1479 // so that we don't waste effort pointlessly scanning for objects. 1480 DCHECK(!marking_deque()->IsFull()); 1481 1482 Map* filler_map = heap()->one_pointer_filler_map(); 1483 for (HeapObject* object = it->Next(); object != NULL; object = it->Next()) { 1484 if ((object->map() != filler_map) && ObjectMarking::IsGrey(object)) { 1485 ObjectMarking::GreyToBlack(object); 1486 PushBlack(object); 1487 if (marking_deque()->IsFull()) return; 1488 } 1489 } 1490 } 1491 1492 void MarkCompactCollector::DiscoverGreyObjectsOnPage(MemoryChunk* p) { 1493 DCHECK(!marking_deque()->IsFull()); 1494 LiveObjectIterator<kGreyObjects> it(p); 1495 HeapObject* object = NULL; 1496 while ((object = it.Next()) != NULL) { 1497 DCHECK(ObjectMarking::IsGrey(object)); 1498 ObjectMarking::GreyToBlack(object); 1499 PushBlack(object); 1500 if (marking_deque()->IsFull()) return; 1501 } 1502 } 1503 1504 class RecordMigratedSlotVisitor final : public ObjectVisitor { 1505 public: 1506 explicit RecordMigratedSlotVisitor(MarkCompactCollector* collector) 1507 : collector_(collector) {} 1508 1509 inline void VisitPointer(Object** p) final { 1510 RecordMigratedSlot(*p, reinterpret_cast<Address>(p)); 1511 } 1512 1513 inline void VisitPointers(Object** start, Object** end) final { 1514 while (start < end) { 1515 RecordMigratedSlot(*start, reinterpret_cast<Address>(start)); 1516 ++start; 1517 } 1518 } 1519 1520 inline void VisitCodeEntry(Address code_entry_slot) final { 1521 Address code_entry = Memory::Address_at(code_entry_slot); 1522 if (Page::FromAddress(code_entry)->IsEvacuationCandidate()) { 1523 RememberedSet<OLD_TO_OLD>::InsertTyped(Page::FromAddress(code_entry_slot), 1524 nullptr, CODE_ENTRY_SLOT, 1525 code_entry_slot); 1526 } 1527 } 1528 1529 inline void VisitCodeTarget(RelocInfo* rinfo) final { 1530 DCHECK(RelocInfo::IsCodeTarget(rinfo->rmode())); 1531 Code* target = Code::GetCodeFromTargetAddress(rinfo->target_address()); 1532 Code* host = rinfo->host(); 1533 // The target is always in old space, we don't have to record the slot in 1534 // the old-to-new remembered set. 1535 DCHECK(!collector_->heap()->InNewSpace(target)); 1536 collector_->RecordRelocSlot(host, rinfo, target); 1537 } 1538 1539 inline void VisitDebugTarget(RelocInfo* rinfo) final { 1540 DCHECK(RelocInfo::IsDebugBreakSlot(rinfo->rmode()) && 1541 rinfo->IsPatchedDebugBreakSlotSequence()); 1542 Code* target = Code::GetCodeFromTargetAddress(rinfo->debug_call_address()); 1543 Code* host = rinfo->host(); 1544 // The target is always in old space, we don't have to record the slot in 1545 // the old-to-new remembered set. 1546 DCHECK(!collector_->heap()->InNewSpace(target)); 1547 collector_->RecordRelocSlot(host, rinfo, target); 1548 } 1549 1550 inline void VisitEmbeddedPointer(RelocInfo* rinfo) final { 1551 DCHECK(rinfo->rmode() == RelocInfo::EMBEDDED_OBJECT); 1552 HeapObject* object = HeapObject::cast(rinfo->target_object()); 1553 Code* host = rinfo->host(); 1554 collector_->heap()->RecordWriteIntoCode(host, rinfo, object); 1555 collector_->RecordRelocSlot(host, rinfo, object); 1556 } 1557 1558 inline void VisitCell(RelocInfo* rinfo) final { 1559 DCHECK(rinfo->rmode() == RelocInfo::CELL); 1560 Cell* cell = rinfo->target_cell(); 1561 Code* host = rinfo->host(); 1562 // The cell is always in old space, we don't have to record the slot in 1563 // the old-to-new remembered set. 1564 DCHECK(!collector_->heap()->InNewSpace(cell)); 1565 collector_->RecordRelocSlot(host, rinfo, cell); 1566 } 1567 1568 // Entries that will never move. 1569 inline void VisitCodeAgeSequence(RelocInfo* rinfo) final { 1570 DCHECK(RelocInfo::IsCodeAgeSequence(rinfo->rmode())); 1571 Code* stub = rinfo->code_age_stub(); 1572 USE(stub); 1573 DCHECK(!Page::FromAddress(stub->address())->IsEvacuationCandidate()); 1574 } 1575 1576 // Entries that are skipped for recording. 1577 inline void VisitExternalReference(RelocInfo* rinfo) final {} 1578 inline void VisitExternalReference(Address* p) final {} 1579 inline void VisitRuntimeEntry(RelocInfo* rinfo) final {} 1580 inline void VisitExternalOneByteString( 1581 v8::String::ExternalOneByteStringResource** resource) final {} 1582 inline void VisitExternalTwoByteString( 1583 v8::String::ExternalStringResource** resource) final {} 1584 inline void VisitInternalReference(RelocInfo* rinfo) final {} 1585 inline void VisitEmbedderReference(Object** p, uint16_t class_id) final {} 1586 1587 private: 1588 inline void RecordMigratedSlot(Object* value, Address slot) { 1589 if (value->IsHeapObject()) { 1590 Page* p = Page::FromAddress(reinterpret_cast<Address>(value)); 1591 if (p->InNewSpace()) { 1592 RememberedSet<OLD_TO_NEW>::Insert(Page::FromAddress(slot), slot); 1593 } else if (p->IsEvacuationCandidate()) { 1594 RememberedSet<OLD_TO_OLD>::Insert(Page::FromAddress(slot), slot); 1595 } 1596 } 1597 } 1598 1599 MarkCompactCollector* collector_; 1600 }; 1601 1602 class MarkCompactCollector::HeapObjectVisitor { 1603 public: 1604 virtual ~HeapObjectVisitor() {} 1605 virtual bool Visit(HeapObject* object) = 0; 1606 }; 1607 1608 class MarkCompactCollector::EvacuateVisitorBase 1609 : public MarkCompactCollector::HeapObjectVisitor { 1610 protected: 1611 enum MigrationMode { kFast, kProfiled }; 1612 1613 EvacuateVisitorBase(Heap* heap, CompactionSpaceCollection* compaction_spaces) 1614 : heap_(heap), 1615 compaction_spaces_(compaction_spaces), 1616 profiling_( 1617 heap->isolate()->is_profiling() || 1618 heap->isolate()->logger()->is_logging_code_events() || 1619 heap->isolate()->heap_profiler()->is_tracking_object_moves()) {} 1620 1621 inline bool TryEvacuateObject(PagedSpace* target_space, HeapObject* object, 1622 HeapObject** target_object) { 1623 #ifdef VERIFY_HEAP 1624 if (AbortCompactionForTesting(object)) return false; 1625 #endif // VERIFY_HEAP 1626 int size = object->Size(); 1627 AllocationAlignment alignment = object->RequiredAlignment(); 1628 AllocationResult allocation = target_space->AllocateRaw(size, alignment); 1629 if (allocation.To(target_object)) { 1630 MigrateObject(*target_object, object, size, target_space->identity()); 1631 return true; 1632 } 1633 return false; 1634 } 1635 1636 inline void MigrateObject(HeapObject* dst, HeapObject* src, int size, 1637 AllocationSpace dest) { 1638 if (profiling_) { 1639 MigrateObject<kProfiled>(dst, src, size, dest); 1640 } else { 1641 MigrateObject<kFast>(dst, src, size, dest); 1642 } 1643 } 1644 1645 template <MigrationMode mode> 1646 inline void MigrateObject(HeapObject* dst, HeapObject* src, int size, 1647 AllocationSpace dest) { 1648 Address dst_addr = dst->address(); 1649 Address src_addr = src->address(); 1650 DCHECK(heap_->AllowedToBeMigrated(src, dest)); 1651 DCHECK(dest != LO_SPACE); 1652 if (dest == OLD_SPACE) { 1653 DCHECK_OBJECT_SIZE(size); 1654 DCHECK(IsAligned(size, kPointerSize)); 1655 heap_->CopyBlock(dst_addr, src_addr, size); 1656 if ((mode == kProfiled) && dst->IsBytecodeArray()) { 1657 PROFILE(heap_->isolate(), 1658 CodeMoveEvent(AbstractCode::cast(src), dst_addr)); 1659 } 1660 RecordMigratedSlotVisitor visitor(heap_->mark_compact_collector()); 1661 dst->IterateBodyFast(dst->map()->instance_type(), size, &visitor); 1662 } else if (dest == CODE_SPACE) { 1663 DCHECK_CODEOBJECT_SIZE(size, heap_->code_space()); 1664 if (mode == kProfiled) { 1665 PROFILE(heap_->isolate(), 1666 CodeMoveEvent(AbstractCode::cast(src), dst_addr)); 1667 } 1668 heap_->CopyBlock(dst_addr, src_addr, size); 1669 Code::cast(dst)->Relocate(dst_addr - src_addr); 1670 RecordMigratedSlotVisitor visitor(heap_->mark_compact_collector()); 1671 dst->IterateBodyFast(dst->map()->instance_type(), size, &visitor); 1672 } else { 1673 DCHECK_OBJECT_SIZE(size); 1674 DCHECK(dest == NEW_SPACE); 1675 heap_->CopyBlock(dst_addr, src_addr, size); 1676 } 1677 if (mode == kProfiled) { 1678 heap_->OnMoveEvent(dst, src, size); 1679 } 1680 Memory::Address_at(src_addr) = dst_addr; 1681 } 1682 1683 #ifdef VERIFY_HEAP 1684 bool AbortCompactionForTesting(HeapObject* object) { 1685 if (FLAG_stress_compaction) { 1686 const uintptr_t mask = static_cast<uintptr_t>(FLAG_random_seed) & 1687 Page::kPageAlignmentMask & ~kPointerAlignmentMask; 1688 if ((reinterpret_cast<uintptr_t>(object->address()) & 1689 Page::kPageAlignmentMask) == mask) { 1690 Page* page = Page::FromAddress(object->address()); 1691 if (page->IsFlagSet(Page::COMPACTION_WAS_ABORTED_FOR_TESTING)) { 1692 page->ClearFlag(Page::COMPACTION_WAS_ABORTED_FOR_TESTING); 1693 } else { 1694 page->SetFlag(Page::COMPACTION_WAS_ABORTED_FOR_TESTING); 1695 return true; 1696 } 1697 } 1698 } 1699 return false; 1700 } 1701 #endif // VERIFY_HEAP 1702 1703 Heap* heap_; 1704 CompactionSpaceCollection* compaction_spaces_; 1705 bool profiling_; 1706 }; 1707 1708 class MarkCompactCollector::EvacuateNewSpaceVisitor final 1709 : public MarkCompactCollector::EvacuateVisitorBase { 1710 public: 1711 static const intptr_t kLabSize = 4 * KB; 1712 static const intptr_t kMaxLabObjectSize = 256; 1713 1714 explicit EvacuateNewSpaceVisitor(Heap* heap, 1715 CompactionSpaceCollection* compaction_spaces, 1716 base::HashMap* local_pretenuring_feedback) 1717 : EvacuateVisitorBase(heap, compaction_spaces), 1718 buffer_(LocalAllocationBuffer::InvalidBuffer()), 1719 space_to_allocate_(NEW_SPACE), 1720 promoted_size_(0), 1721 semispace_copied_size_(0), 1722 local_pretenuring_feedback_(local_pretenuring_feedback) {} 1723 1724 inline bool Visit(HeapObject* object) override { 1725 heap_->UpdateAllocationSite<Heap::kCached>(object, 1726 local_pretenuring_feedback_); 1727 int size = object->Size(); 1728 HeapObject* target_object = nullptr; 1729 if (heap_->ShouldBePromoted(object->address(), size) && 1730 TryEvacuateObject(compaction_spaces_->Get(OLD_SPACE), object, 1731 &target_object)) { 1732 promoted_size_ += size; 1733 return true; 1734 } 1735 HeapObject* target = nullptr; 1736 AllocationSpace space = AllocateTargetObject(object, &target); 1737 MigrateObject(HeapObject::cast(target), object, size, space); 1738 semispace_copied_size_ += size; 1739 return true; 1740 } 1741 1742 intptr_t promoted_size() { return promoted_size_; } 1743 intptr_t semispace_copied_size() { return semispace_copied_size_; } 1744 1745 private: 1746 enum NewSpaceAllocationMode { 1747 kNonstickyBailoutOldSpace, 1748 kStickyBailoutOldSpace, 1749 }; 1750 1751 inline AllocationSpace AllocateTargetObject(HeapObject* old_object, 1752 HeapObject** target_object) { 1753 const int size = old_object->Size(); 1754 AllocationAlignment alignment = old_object->RequiredAlignment(); 1755 AllocationResult allocation; 1756 AllocationSpace space_allocated_in = space_to_allocate_; 1757 if (space_to_allocate_ == NEW_SPACE) { 1758 if (size > kMaxLabObjectSize) { 1759 allocation = 1760 AllocateInNewSpace(size, alignment, kNonstickyBailoutOldSpace); 1761 } else { 1762 allocation = AllocateInLab(size, alignment); 1763 } 1764 } 1765 if (allocation.IsRetry() || (space_to_allocate_ == OLD_SPACE)) { 1766 allocation = AllocateInOldSpace(size, alignment); 1767 space_allocated_in = OLD_SPACE; 1768 } 1769 bool ok = allocation.To(target_object); 1770 DCHECK(ok); 1771 USE(ok); 1772 return space_allocated_in; 1773 } 1774 1775 inline bool NewLocalAllocationBuffer() { 1776 AllocationResult result = 1777 AllocateInNewSpace(kLabSize, kWordAligned, kStickyBailoutOldSpace); 1778 LocalAllocationBuffer saved_old_buffer = buffer_; 1779 buffer_ = LocalAllocationBuffer::FromResult(heap_, result, kLabSize); 1780 if (buffer_.IsValid()) { 1781 buffer_.TryMerge(&saved_old_buffer); 1782 return true; 1783 } 1784 return false; 1785 } 1786 1787 inline AllocationResult AllocateInNewSpace(int size_in_bytes, 1788 AllocationAlignment alignment, 1789 NewSpaceAllocationMode mode) { 1790 AllocationResult allocation = 1791 heap_->new_space()->AllocateRawSynchronized(size_in_bytes, alignment); 1792 if (allocation.IsRetry()) { 1793 if (!heap_->new_space()->AddFreshPageSynchronized()) { 1794 if (mode == kStickyBailoutOldSpace) space_to_allocate_ = OLD_SPACE; 1795 } else { 1796 allocation = heap_->new_space()->AllocateRawSynchronized(size_in_bytes, 1797 alignment); 1798 if (allocation.IsRetry()) { 1799 if (mode == kStickyBailoutOldSpace) space_to_allocate_ = OLD_SPACE; 1800 } 1801 } 1802 } 1803 return allocation; 1804 } 1805 1806 inline AllocationResult AllocateInOldSpace(int size_in_bytes, 1807 AllocationAlignment alignment) { 1808 AllocationResult allocation = 1809 compaction_spaces_->Get(OLD_SPACE)->AllocateRaw(size_in_bytes, 1810 alignment); 1811 if (allocation.IsRetry()) { 1812 v8::internal::Heap::FatalProcessOutOfMemory( 1813 "MarkCompactCollector: semi-space copy, fallback in old gen", true); 1814 } 1815 return allocation; 1816 } 1817 1818 inline AllocationResult AllocateInLab(int size_in_bytes, 1819 AllocationAlignment alignment) { 1820 AllocationResult allocation; 1821 if (!buffer_.IsValid()) { 1822 if (!NewLocalAllocationBuffer()) { 1823 space_to_allocate_ = OLD_SPACE; 1824 return AllocationResult::Retry(OLD_SPACE); 1825 } 1826 } 1827 allocation = buffer_.AllocateRawAligned(size_in_bytes, alignment); 1828 if (allocation.IsRetry()) { 1829 if (!NewLocalAllocationBuffer()) { 1830 space_to_allocate_ = OLD_SPACE; 1831 return AllocationResult::Retry(OLD_SPACE); 1832 } else { 1833 allocation = buffer_.AllocateRawAligned(size_in_bytes, alignment); 1834 if (allocation.IsRetry()) { 1835 space_to_allocate_ = OLD_SPACE; 1836 return AllocationResult::Retry(OLD_SPACE); 1837 } 1838 } 1839 } 1840 return allocation; 1841 } 1842 1843 LocalAllocationBuffer buffer_; 1844 AllocationSpace space_to_allocate_; 1845 intptr_t promoted_size_; 1846 intptr_t semispace_copied_size_; 1847 base::HashMap* local_pretenuring_feedback_; 1848 }; 1849 1850 template <PageEvacuationMode mode> 1851 class MarkCompactCollector::EvacuateNewSpacePageVisitor final 1852 : public MarkCompactCollector::HeapObjectVisitor { 1853 public: 1854 explicit EvacuateNewSpacePageVisitor( 1855 Heap* heap, base::HashMap* local_pretenuring_feedback) 1856 : heap_(heap), 1857 moved_bytes_(0), 1858 local_pretenuring_feedback_(local_pretenuring_feedback) {} 1859 1860 static void Move(Page* page) { 1861 switch (mode) { 1862 case NEW_TO_NEW: 1863 page->heap()->new_space()->MovePageFromSpaceToSpace(page); 1864 page->SetFlag(Page::PAGE_NEW_NEW_PROMOTION); 1865 break; 1866 case NEW_TO_OLD: { 1867 page->Unlink(); 1868 Page* new_page = Page::ConvertNewToOld(page); 1869 new_page->SetFlag(Page::PAGE_NEW_OLD_PROMOTION); 1870 break; 1871 } 1872 } 1873 } 1874 1875 inline bool Visit(HeapObject* object) { 1876 heap_->UpdateAllocationSite<Heap::kCached>(object, 1877 local_pretenuring_feedback_); 1878 if (mode == NEW_TO_OLD) { 1879 RecordMigratedSlotVisitor visitor(heap_->mark_compact_collector()); 1880 object->IterateBodyFast(&visitor); 1881 } 1882 return true; 1883 } 1884 1885 intptr_t moved_bytes() { return moved_bytes_; } 1886 void account_moved_bytes(intptr_t bytes) { moved_bytes_ += bytes; } 1887 1888 private: 1889 Heap* heap_; 1890 intptr_t moved_bytes_; 1891 base::HashMap* local_pretenuring_feedback_; 1892 }; 1893 1894 class MarkCompactCollector::EvacuateOldSpaceVisitor final 1895 : public MarkCompactCollector::EvacuateVisitorBase { 1896 public: 1897 EvacuateOldSpaceVisitor(Heap* heap, 1898 CompactionSpaceCollection* compaction_spaces) 1899 : EvacuateVisitorBase(heap, compaction_spaces) {} 1900 1901 inline bool Visit(HeapObject* object) override { 1902 CompactionSpace* target_space = compaction_spaces_->Get( 1903 Page::FromAddress(object->address())->owner()->identity()); 1904 HeapObject* target_object = nullptr; 1905 if (TryEvacuateObject(target_space, object, &target_object)) { 1906 DCHECK(object->map_word().IsForwardingAddress()); 1907 return true; 1908 } 1909 return false; 1910 } 1911 }; 1912 1913 class MarkCompactCollector::EvacuateRecordOnlyVisitor final 1914 : public MarkCompactCollector::HeapObjectVisitor { 1915 public: 1916 explicit EvacuateRecordOnlyVisitor(Heap* heap) : heap_(heap) {} 1917 1918 inline bool Visit(HeapObject* object) { 1919 RecordMigratedSlotVisitor visitor(heap_->mark_compact_collector()); 1920 object->IterateBody(&visitor); 1921 return true; 1922 } 1923 1924 private: 1925 Heap* heap_; 1926 }; 1927 1928 void MarkCompactCollector::DiscoverGreyObjectsInSpace(PagedSpace* space) { 1929 for (Page* p : *space) { 1930 DiscoverGreyObjectsOnPage(p); 1931 if (marking_deque()->IsFull()) return; 1932 } 1933 } 1934 1935 1936 void MarkCompactCollector::DiscoverGreyObjectsInNewSpace() { 1937 NewSpace* space = heap()->new_space(); 1938 for (Page* page : PageRange(space->bottom(), space->top())) { 1939 DiscoverGreyObjectsOnPage(page); 1940 if (marking_deque()->IsFull()) return; 1941 } 1942 } 1943 1944 1945 bool MarkCompactCollector::IsUnmarkedHeapObject(Object** p) { 1946 Object* o = *p; 1947 if (!o->IsHeapObject()) return false; 1948 return ObjectMarking::IsWhite(HeapObject::cast(o)); 1949 } 1950 1951 1952 bool MarkCompactCollector::IsUnmarkedHeapObjectWithHeap(Heap* heap, 1953 Object** p) { 1954 Object* o = *p; 1955 DCHECK(o->IsHeapObject()); 1956 return ObjectMarking::IsWhite(HeapObject::cast(o)); 1957 } 1958 1959 void MarkCompactCollector::MarkStringTable( 1960 RootMarkingVisitor<MarkCompactMode::FULL>* visitor) { 1961 StringTable* string_table = heap()->string_table(); 1962 // Mark the string table itself. 1963 if (ObjectMarking::IsWhite(string_table)) { 1964 // String table could have already been marked by visiting the handles list. 1965 ObjectMarking::WhiteToBlack(string_table); 1966 } 1967 // Explicitly mark the prefix. 1968 string_table->IteratePrefix(visitor); 1969 ProcessMarkingDeque<MarkCompactMode::FULL>(); 1970 } 1971 1972 void MarkCompactCollector::MarkRoots( 1973 RootMarkingVisitor<MarkCompactMode::FULL>* visitor) { 1974 // Mark the heap roots including global variables, stack variables, 1975 // etc., and all objects reachable from them. 1976 heap()->IterateStrongRoots(visitor, VISIT_ONLY_STRONG); 1977 1978 // Handle the string table specially. 1979 MarkStringTable(visitor); 1980 1981 // There may be overflowed objects in the heap. Visit them now. 1982 while (marking_deque()->overflowed()) { 1983 RefillMarkingDeque<MarkCompactMode::FULL>(); 1984 EmptyMarkingDeque<MarkCompactMode::FULL>(); 1985 } 1986 } 1987 1988 1989 void MarkCompactCollector::MarkImplicitRefGroups( 1990 MarkObjectFunction mark_object) { 1991 List<ImplicitRefGroup*>* ref_groups = 1992 isolate()->global_handles()->implicit_ref_groups(); 1993 1994 int last = 0; 1995 for (int i = 0; i < ref_groups->length(); i++) { 1996 ImplicitRefGroup* entry = ref_groups->at(i); 1997 DCHECK(entry != NULL); 1998 1999 if (ObjectMarking::IsWhite(*entry->parent)) { 2000 (*ref_groups)[last++] = entry; 2001 continue; 2002 } 2003 2004 Object*** children = entry->children; 2005 // A parent object is marked, so mark all child heap objects. 2006 for (size_t j = 0; j < entry->length; ++j) { 2007 if ((*children[j])->IsHeapObject()) { 2008 mark_object(heap(), HeapObject::cast(*children[j])); 2009 } 2010 } 2011 2012 // Once the entire group has been marked, dispose it because it's 2013 // not needed anymore. 2014 delete entry; 2015 } 2016 ref_groups->Rewind(last); 2017 } 2018 2019 2020 // Mark all objects reachable from the objects on the marking stack. 2021 // Before: the marking stack contains zero or more heap object pointers. 2022 // After: the marking stack is empty, and all objects reachable from the 2023 // marking stack have been marked, or are overflowed in the heap. 2024 template <MarkCompactMode mode> 2025 void MarkCompactCollector::EmptyMarkingDeque() { 2026 while (!marking_deque()->IsEmpty()) { 2027 HeapObject* object = marking_deque()->Pop(); 2028 2029 DCHECK(!object->IsFiller()); 2030 DCHECK(object->IsHeapObject()); 2031 DCHECK(heap()->Contains(object)); 2032 DCHECK(!ObjectMarking::IsWhite(object)); 2033 2034 Map* map = object->map(); 2035 switch (mode) { 2036 case MarkCompactMode::FULL: { 2037 MarkObject(map); 2038 MarkCompactMarkingVisitor::IterateBody(map, object); 2039 } break; 2040 case MarkCompactMode::YOUNG_GENERATION: { 2041 DCHECK(ObjectMarking::IsBlack(object)); 2042 StaticYoungGenerationMarkingVisitor::IterateBody(map, object); 2043 } break; 2044 } 2045 } 2046 } 2047 2048 2049 // Sweep the heap for overflowed objects, clear their overflow bits, and 2050 // push them on the marking stack. Stop early if the marking stack fills 2051 // before sweeping completes. If sweeping completes, there are no remaining 2052 // overflowed objects in the heap so the overflow flag on the markings stack 2053 // is cleared. 2054 template <MarkCompactMode mode> 2055 void MarkCompactCollector::RefillMarkingDeque() { 2056 isolate()->CountUsage(v8::Isolate::UseCounterFeature::kMarkDequeOverflow); 2057 DCHECK(marking_deque()->overflowed()); 2058 2059 DiscoverGreyObjectsInNewSpace(); 2060 if (marking_deque()->IsFull()) return; 2061 2062 if (mode == MarkCompactMode::FULL) { 2063 DiscoverGreyObjectsInSpace(heap()->old_space()); 2064 if (marking_deque()->IsFull()) return; 2065 DiscoverGreyObjectsInSpace(heap()->code_space()); 2066 if (marking_deque()->IsFull()) return; 2067 DiscoverGreyObjectsInSpace(heap()->map_space()); 2068 if (marking_deque()->IsFull()) return; 2069 LargeObjectIterator lo_it(heap()->lo_space()); 2070 DiscoverGreyObjectsWithIterator(&lo_it); 2071 if (marking_deque()->IsFull()) return; 2072 } 2073 2074 marking_deque()->ClearOverflowed(); 2075 } 2076 2077 2078 // Mark all objects reachable (transitively) from objects on the marking 2079 // stack. Before: the marking stack contains zero or more heap object 2080 // pointers. After: the marking stack is empty and there are no overflowed 2081 // objects in the heap. 2082 template <MarkCompactMode mode> 2083 void MarkCompactCollector::ProcessMarkingDeque() { 2084 EmptyMarkingDeque<mode>(); 2085 while (marking_deque()->overflowed()) { 2086 RefillMarkingDeque<mode>(); 2087 EmptyMarkingDeque<mode>(); 2088 } 2089 DCHECK(marking_deque()->IsEmpty()); 2090 } 2091 2092 // Mark all objects reachable (transitively) from objects on the marking 2093 // stack including references only considered in the atomic marking pause. 2094 void MarkCompactCollector::ProcessEphemeralMarking( 2095 ObjectVisitor* visitor, bool only_process_harmony_weak_collections) { 2096 DCHECK(marking_deque()->IsEmpty() && !marking_deque()->overflowed()); 2097 bool work_to_do = true; 2098 while (work_to_do) { 2099 if (!only_process_harmony_weak_collections) { 2100 if (heap_->local_embedder_heap_tracer()->InUse()) { 2101 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WRAPPER_TRACING); 2102 heap_->local_embedder_heap_tracer()->RegisterWrappersWithRemoteTracer(); 2103 heap_->local_embedder_heap_tracer()->Trace( 2104 0, 2105 EmbedderHeapTracer::AdvanceTracingActions( 2106 EmbedderHeapTracer::ForceCompletionAction::FORCE_COMPLETION)); 2107 } else { 2108 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_OBJECT_GROUPING); 2109 isolate()->global_handles()->IterateObjectGroups( 2110 visitor, &IsUnmarkedHeapObjectWithHeap); 2111 MarkImplicitRefGroups(&MarkCompactMarkingVisitor::MarkObject); 2112 } 2113 } else { 2114 // TODO(mlippautz): We currently do not trace through blink when 2115 // discovering new objects reachable from weak roots (that have been made 2116 // strong). This is a limitation of not having a separate handle type 2117 // that doesn't require zapping before this phase. See crbug.com/668060. 2118 heap_->local_embedder_heap_tracer()->ClearCachedWrappersToTrace(); 2119 } 2120 ProcessWeakCollections(); 2121 work_to_do = !marking_deque()->IsEmpty(); 2122 ProcessMarkingDeque<MarkCompactMode::FULL>(); 2123 } 2124 CHECK(marking_deque()->IsEmpty()); 2125 CHECK_EQ(0, heap()->local_embedder_heap_tracer()->NumberOfWrappersToTrace()); 2126 } 2127 2128 void MarkCompactCollector::ProcessTopOptimizedFrame(ObjectVisitor* visitor) { 2129 for (StackFrameIterator it(isolate(), isolate()->thread_local_top()); 2130 !it.done(); it.Advance()) { 2131 if (it.frame()->type() == StackFrame::JAVA_SCRIPT) { 2132 return; 2133 } 2134 if (it.frame()->type() == StackFrame::OPTIMIZED) { 2135 Code* code = it.frame()->LookupCode(); 2136 if (!code->CanDeoptAt(it.frame()->pc())) { 2137 Code::BodyDescriptor::IterateBody(code, visitor); 2138 } 2139 ProcessMarkingDeque<MarkCompactMode::FULL>(); 2140 return; 2141 } 2142 } 2143 } 2144 2145 void MarkingDeque::SetUp() { 2146 backing_store_ = new base::VirtualMemory(kMaxSize); 2147 backing_store_committed_size_ = 0; 2148 if (backing_store_ == nullptr) { 2149 V8::FatalProcessOutOfMemory("MarkingDeque::SetUp"); 2150 } 2151 } 2152 2153 void MarkingDeque::TearDown() { 2154 delete backing_store_; 2155 } 2156 2157 void MarkingDeque::StartUsing() { 2158 base::LockGuard<base::Mutex> guard(&mutex_); 2159 if (in_use_) { 2160 // This can happen in mark-compact GC if the incremental marker already 2161 // started using the marking deque. 2162 return; 2163 } 2164 in_use_ = true; 2165 EnsureCommitted(); 2166 array_ = reinterpret_cast<HeapObject**>(backing_store_->address()); 2167 size_t size = FLAG_force_marking_deque_overflows 2168 ? 64 * kPointerSize 2169 : backing_store_committed_size_; 2170 DCHECK( 2171 base::bits::IsPowerOfTwo32(static_cast<uint32_t>(size / kPointerSize))); 2172 mask_ = static_cast<int>((size / kPointerSize) - 1); 2173 top_ = bottom_ = 0; 2174 overflowed_ = false; 2175 } 2176 2177 void MarkingDeque::StopUsing() { 2178 base::LockGuard<base::Mutex> guard(&mutex_); 2179 if (!in_use_) return; 2180 DCHECK(IsEmpty()); 2181 DCHECK(!overflowed_); 2182 top_ = bottom_ = mask_ = 0; 2183 in_use_ = false; 2184 if (FLAG_concurrent_sweeping) { 2185 StartUncommitTask(); 2186 } else { 2187 Uncommit(); 2188 } 2189 } 2190 2191 void MarkingDeque::Clear() { 2192 DCHECK(in_use_); 2193 top_ = bottom_ = 0; 2194 overflowed_ = false; 2195 } 2196 2197 void MarkingDeque::Uncommit() { 2198 DCHECK(!in_use_); 2199 bool success = backing_store_->Uncommit(backing_store_->address(), 2200 backing_store_committed_size_); 2201 backing_store_committed_size_ = 0; 2202 CHECK(success); 2203 } 2204 2205 void MarkingDeque::EnsureCommitted() { 2206 DCHECK(in_use_); 2207 if (backing_store_committed_size_ > 0) return; 2208 2209 for (size_t size = kMaxSize; size >= kMinSize; size /= 2) { 2210 if (backing_store_->Commit(backing_store_->address(), size, false)) { 2211 backing_store_committed_size_ = size; 2212 break; 2213 } 2214 } 2215 if (backing_store_committed_size_ == 0) { 2216 V8::FatalProcessOutOfMemory("MarkingDeque::EnsureCommitted"); 2217 } 2218 } 2219 2220 void MarkingDeque::StartUncommitTask() { 2221 if (!uncommit_task_pending_) { 2222 uncommit_task_pending_ = true; 2223 UncommitTask* task = new UncommitTask(heap_->isolate(), this); 2224 V8::GetCurrentPlatform()->CallOnBackgroundThread( 2225 task, v8::Platform::kShortRunningTask); 2226 } 2227 } 2228 2229 class MarkCompactCollector::ObjectStatsVisitor 2230 : public MarkCompactCollector::HeapObjectVisitor { 2231 public: 2232 ObjectStatsVisitor(Heap* heap, ObjectStats* live_stats, 2233 ObjectStats* dead_stats) 2234 : live_collector_(heap, live_stats), dead_collector_(heap, dead_stats) { 2235 DCHECK_NOT_NULL(live_stats); 2236 DCHECK_NOT_NULL(dead_stats); 2237 // Global objects are roots and thus recorded as live. 2238 live_collector_.CollectGlobalStatistics(); 2239 } 2240 2241 bool Visit(HeapObject* obj) override { 2242 if (ObjectMarking::IsBlack(obj)) { 2243 live_collector_.CollectStatistics(obj); 2244 } else { 2245 DCHECK(!ObjectMarking::IsGrey(obj)); 2246 dead_collector_.CollectStatistics(obj); 2247 } 2248 return true; 2249 } 2250 2251 private: 2252 ObjectStatsCollector live_collector_; 2253 ObjectStatsCollector dead_collector_; 2254 }; 2255 2256 void MarkCompactCollector::VisitAllObjects(HeapObjectVisitor* visitor) { 2257 SpaceIterator space_it(heap()); 2258 HeapObject* obj = nullptr; 2259 while (space_it.has_next()) { 2260 std::unique_ptr<ObjectIterator> it(space_it.next()->GetObjectIterator()); 2261 ObjectIterator* obj_it = it.get(); 2262 while ((obj = obj_it->Next()) != nullptr) { 2263 visitor->Visit(obj); 2264 } 2265 } 2266 } 2267 2268 void MarkCompactCollector::RecordObjectStats() { 2269 if (V8_UNLIKELY(FLAG_gc_stats)) { 2270 heap()->CreateObjectStats(); 2271 ObjectStatsVisitor visitor(heap(), heap()->live_object_stats_, 2272 heap()->dead_object_stats_); 2273 VisitAllObjects(&visitor); 2274 if (V8_UNLIKELY(FLAG_gc_stats & 2275 v8::tracing::TracingCategoryObserver::ENABLED_BY_TRACING)) { 2276 std::stringstream live, dead; 2277 heap()->live_object_stats_->Dump(live); 2278 heap()->dead_object_stats_->Dump(dead); 2279 TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("v8.gc_stats"), 2280 "V8.GC_Objects_Stats", TRACE_EVENT_SCOPE_THREAD, 2281 "live", TRACE_STR_COPY(live.str().c_str()), "dead", 2282 TRACE_STR_COPY(dead.str().c_str())); 2283 } 2284 if (FLAG_trace_gc_object_stats) { 2285 heap()->live_object_stats_->PrintJSON("live"); 2286 heap()->dead_object_stats_->PrintJSON("dead"); 2287 } 2288 heap()->live_object_stats_->CheckpointObjectStats(); 2289 heap()->dead_object_stats_->ClearObjectStats(); 2290 } 2291 } 2292 2293 SlotCallbackResult MarkCompactCollector::CheckAndMarkObject( 2294 Heap* heap, Address slot_address) { 2295 Object* object = *reinterpret_cast<Object**>(slot_address); 2296 if (heap->InNewSpace(object)) { 2297 // Marking happens before flipping the young generation, so the object 2298 // has to be in ToSpace. 2299 DCHECK(heap->InToSpace(object)); 2300 HeapObject* heap_object = reinterpret_cast<HeapObject*>(object); 2301 if (ObjectMarking::IsBlackOrGrey(heap_object)) { 2302 return KEEP_SLOT; 2303 } 2304 ObjectMarking::WhiteToBlack(heap_object); 2305 StaticYoungGenerationMarkingVisitor::IterateBody(heap_object->map(), 2306 heap_object); 2307 return KEEP_SLOT; 2308 } 2309 return REMOVE_SLOT; 2310 } 2311 2312 static bool IsUnmarkedObject(Heap* heap, Object** p) { 2313 DCHECK_IMPLIES(heap->InNewSpace(*p), heap->InToSpace(*p)); 2314 return heap->InNewSpace(*p) && !ObjectMarking::IsBlack(HeapObject::cast(*p)); 2315 } 2316 2317 void MarkCompactCollector::MarkLiveObjectsInYoungGeneration() { 2318 TRACE_GC(heap()->tracer(), GCTracer::Scope::MINOR_MC_MARK); 2319 2320 PostponeInterruptsScope postpone(isolate()); 2321 2322 StaticYoungGenerationMarkingVisitor::Initialize(heap()); 2323 RootMarkingVisitor<MarkCompactMode::YOUNG_GENERATION> root_visitor(heap()); 2324 2325 marking_deque()->StartUsing(); 2326 2327 isolate()->global_handles()->IdentifyWeakUnmodifiedObjects( 2328 &Heap::IsUnmodifiedHeapObject); 2329 2330 { 2331 TRACE_GC(heap()->tracer(), GCTracer::Scope::MINOR_MC_MARK_ROOTS); 2332 heap()->IterateRoots(&root_visitor, VISIT_ALL_IN_SCAVENGE); 2333 ProcessMarkingDeque<MarkCompactMode::YOUNG_GENERATION>(); 2334 } 2335 2336 { 2337 TRACE_GC(heap()->tracer(), 2338 GCTracer::Scope::MINOR_MC_MARK_OLD_TO_NEW_POINTERS); 2339 RememberedSet<OLD_TO_NEW>::Iterate(heap(), [this](Address addr) { 2340 return CheckAndMarkObject(heap(), addr); 2341 }); 2342 RememberedSet<OLD_TO_NEW>::IterateTyped( 2343 heap(), [this](SlotType type, Address host_addr, Address addr) { 2344 return UpdateTypedSlotHelper::UpdateTypedSlot( 2345 isolate(), type, addr, [this](Object** addr) { 2346 return CheckAndMarkObject(heap(), 2347 reinterpret_cast<Address>(addr)); 2348 }); 2349 }); 2350 ProcessMarkingDeque<MarkCompactMode::YOUNG_GENERATION>(); 2351 } 2352 2353 { 2354 TRACE_GC(heap()->tracer(), GCTracer::Scope::MINOR_MC_MARK_WEAK); 2355 heap()->VisitEncounteredWeakCollections(&root_visitor); 2356 ProcessMarkingDeque<MarkCompactMode::YOUNG_GENERATION>(); 2357 } 2358 2359 if (is_code_flushing_enabled()) { 2360 TRACE_GC(heap()->tracer(), 2361 GCTracer::Scope::MINOR_MC_MARK_CODE_FLUSH_CANDIDATES); 2362 code_flusher()->IteratePointersToFromSpace(&root_visitor); 2363 ProcessMarkingDeque<MarkCompactMode::YOUNG_GENERATION>(); 2364 } 2365 2366 { 2367 TRACE_GC(heap()->tracer(), GCTracer::Scope::MINOR_MC_MARK_GLOBAL_HANDLES); 2368 isolate()->global_handles()->MarkNewSpaceWeakUnmodifiedObjectsPending( 2369 &IsUnmarkedObject); 2370 isolate() 2371 ->global_handles() 2372 ->IterateNewSpaceWeakUnmodifiedRoots<GlobalHandles::VISIT_OTHERS>( 2373 &root_visitor); 2374 ProcessMarkingDeque<MarkCompactMode::YOUNG_GENERATION>(); 2375 } 2376 2377 marking_deque()->StopUsing(); 2378 } 2379 2380 void MarkCompactCollector::MarkLiveObjects() { 2381 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK); 2382 // The recursive GC marker detects when it is nearing stack overflow, 2383 // and switches to a different marking system. JS interrupts interfere 2384 // with the C stack limit check. 2385 PostponeInterruptsScope postpone(isolate()); 2386 2387 { 2388 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_FINISH_INCREMENTAL); 2389 IncrementalMarking* incremental_marking = heap_->incremental_marking(); 2390 if (was_marked_incrementally_) { 2391 incremental_marking->Finalize(); 2392 } else { 2393 CHECK(incremental_marking->IsStopped()); 2394 } 2395 } 2396 2397 #ifdef DEBUG 2398 DCHECK(state_ == PREPARE_GC); 2399 state_ = MARK_LIVE_OBJECTS; 2400 #endif 2401 2402 marking_deque()->StartUsing(); 2403 2404 heap_->local_embedder_heap_tracer()->EnterFinalPause(); 2405 2406 { 2407 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_PREPARE_CODE_FLUSH); 2408 PrepareForCodeFlushing(); 2409 } 2410 2411 RootMarkingVisitor<MarkCompactMode::FULL> root_visitor(heap()); 2412 2413 { 2414 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_ROOTS); 2415 MarkRoots(&root_visitor); 2416 ProcessTopOptimizedFrame(&root_visitor); 2417 } 2418 2419 { 2420 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WEAK_CLOSURE); 2421 2422 // The objects reachable from the roots are marked, yet unreachable 2423 // objects are unmarked. Mark objects reachable due to host 2424 // application specific logic or through Harmony weak maps. 2425 { 2426 TRACE_GC(heap()->tracer(), 2427 GCTracer::Scope::MC_MARK_WEAK_CLOSURE_EPHEMERAL); 2428 ProcessEphemeralMarking(&root_visitor, false); 2429 } 2430 2431 // The objects reachable from the roots, weak maps or object groups 2432 // are marked. Objects pointed to only by weak global handles cannot be 2433 // immediately reclaimed. Instead, we have to mark them as pending and mark 2434 // objects reachable from them. 2435 // 2436 // First we identify nonlive weak handles and mark them as pending 2437 // destruction. 2438 { 2439 TRACE_GC(heap()->tracer(), 2440 GCTracer::Scope::MC_MARK_WEAK_CLOSURE_WEAK_HANDLES); 2441 heap()->isolate()->global_handles()->IdentifyWeakHandles( 2442 &IsUnmarkedHeapObject); 2443 ProcessMarkingDeque<MarkCompactMode::FULL>(); 2444 } 2445 // Then we mark the objects. 2446 2447 { 2448 TRACE_GC(heap()->tracer(), 2449 GCTracer::Scope::MC_MARK_WEAK_CLOSURE_WEAK_ROOTS); 2450 heap()->isolate()->global_handles()->IterateWeakRoots(&root_visitor); 2451 ProcessMarkingDeque<MarkCompactMode::FULL>(); 2452 } 2453 2454 // Repeat Harmony weak maps marking to mark unmarked objects reachable from 2455 // the weak roots we just marked as pending destruction. 2456 // 2457 // We only process harmony collections, as all object groups have been fully 2458 // processed and no weakly reachable node can discover new objects groups. 2459 { 2460 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WEAK_CLOSURE_HARMONY); 2461 ProcessEphemeralMarking(&root_visitor, true); 2462 { 2463 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_MARK_WRAPPER_EPILOGUE); 2464 heap()->local_embedder_heap_tracer()->TraceEpilogue(); 2465 } 2466 } 2467 } 2468 } 2469 2470 2471 void MarkCompactCollector::ClearNonLiveReferences() { 2472 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR); 2473 2474 { 2475 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_STRING_TABLE); 2476 2477 // Prune the string table removing all strings only pointed to by the 2478 // string table. Cannot use string_table() here because the string 2479 // table is marked. 2480 StringTable* string_table = heap()->string_table(); 2481 InternalizedStringTableCleaner internalized_visitor(heap(), string_table); 2482 string_table->IterateElements(&internalized_visitor); 2483 string_table->ElementsRemoved(internalized_visitor.PointersRemoved()); 2484 2485 ExternalStringTableCleaner external_visitor(heap(), nullptr); 2486 heap()->external_string_table_.IterateAll(&external_visitor); 2487 heap()->external_string_table_.CleanUpAll(); 2488 } 2489 2490 { 2491 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_WEAK_LISTS); 2492 // Process the weak references. 2493 MarkCompactWeakObjectRetainer mark_compact_object_retainer; 2494 heap()->ProcessAllWeakReferences(&mark_compact_object_retainer); 2495 } 2496 2497 { 2498 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_GLOBAL_HANDLES); 2499 2500 // Remove object groups after marking phase. 2501 heap()->isolate()->global_handles()->RemoveObjectGroups(); 2502 heap()->isolate()->global_handles()->RemoveImplicitRefGroups(); 2503 } 2504 2505 // Flush code from collected candidates. 2506 if (is_code_flushing_enabled()) { 2507 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_CODE_FLUSH); 2508 code_flusher_->ProcessCandidates(); 2509 } 2510 2511 2512 DependentCode* dependent_code_list; 2513 Object* non_live_map_list; 2514 ClearWeakCells(&non_live_map_list, &dependent_code_list); 2515 2516 { 2517 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_MAPS); 2518 ClearSimpleMapTransitions(non_live_map_list); 2519 ClearFullMapTransitions(); 2520 } 2521 2522 MarkDependentCodeForDeoptimization(dependent_code_list); 2523 2524 ClearWeakCollections(); 2525 } 2526 2527 2528 void MarkCompactCollector::MarkDependentCodeForDeoptimization( 2529 DependentCode* list_head) { 2530 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_DEPENDENT_CODE); 2531 Isolate* isolate = this->isolate(); 2532 DependentCode* current = list_head; 2533 while (current->length() > 0) { 2534 have_code_to_deoptimize_ |= current->MarkCodeForDeoptimization( 2535 isolate, DependentCode::kWeakCodeGroup); 2536 current = current->next_link(); 2537 } 2538 2539 { 2540 ArrayList* list = heap_->weak_new_space_object_to_code_list(); 2541 int counter = 0; 2542 for (int i = 0; i < list->Length(); i += 2) { 2543 WeakCell* obj = WeakCell::cast(list->Get(i)); 2544 WeakCell* dep = WeakCell::cast(list->Get(i + 1)); 2545 if (obj->cleared() || dep->cleared()) { 2546 if (!dep->cleared()) { 2547 Code* code = Code::cast(dep->value()); 2548 if (!code->marked_for_deoptimization()) { 2549 DependentCode::SetMarkedForDeoptimization( 2550 code, DependentCode::DependencyGroup::kWeakCodeGroup); 2551 code->InvalidateEmbeddedObjects(); 2552 have_code_to_deoptimize_ = true; 2553 } 2554 } 2555 } else { 2556 // We record the slot manually because marking is finished at this 2557 // point and the write barrier would bailout. 2558 list->Set(counter, obj, SKIP_WRITE_BARRIER); 2559 RecordSlot(list, list->Slot(counter), obj); 2560 counter++; 2561 list->Set(counter, dep, SKIP_WRITE_BARRIER); 2562 RecordSlot(list, list->Slot(counter), dep); 2563 counter++; 2564 } 2565 } 2566 } 2567 2568 WeakHashTable* table = heap_->weak_object_to_code_table(); 2569 uint32_t capacity = table->Capacity(); 2570 for (uint32_t i = 0; i < capacity; i++) { 2571 uint32_t key_index = table->EntryToIndex(i); 2572 Object* key = table->get(key_index); 2573 if (!table->IsKey(isolate, key)) continue; 2574 uint32_t value_index = table->EntryToValueIndex(i); 2575 Object* value = table->get(value_index); 2576 DCHECK(key->IsWeakCell()); 2577 if (WeakCell::cast(key)->cleared()) { 2578 have_code_to_deoptimize_ |= 2579 DependentCode::cast(value)->MarkCodeForDeoptimization( 2580 isolate, DependentCode::kWeakCodeGroup); 2581 table->set(key_index, heap_->the_hole_value()); 2582 table->set(value_index, heap_->the_hole_value()); 2583 table->ElementRemoved(); 2584 } 2585 } 2586 } 2587 2588 2589 void MarkCompactCollector::ClearSimpleMapTransitions( 2590 Object* non_live_map_list) { 2591 Object* the_hole_value = heap()->the_hole_value(); 2592 Object* weak_cell_obj = non_live_map_list; 2593 while (weak_cell_obj != Smi::kZero) { 2594 WeakCell* weak_cell = WeakCell::cast(weak_cell_obj); 2595 Map* map = Map::cast(weak_cell->value()); 2596 DCHECK(ObjectMarking::IsWhite(map)); 2597 Object* potential_parent = map->constructor_or_backpointer(); 2598 if (potential_parent->IsMap()) { 2599 Map* parent = Map::cast(potential_parent); 2600 if (ObjectMarking::IsBlackOrGrey(parent) && 2601 parent->raw_transitions() == weak_cell) { 2602 ClearSimpleMapTransition(parent, map); 2603 } 2604 } 2605 weak_cell->clear(); 2606 weak_cell_obj = weak_cell->next(); 2607 weak_cell->clear_next(the_hole_value); 2608 } 2609 } 2610 2611 2612 void MarkCompactCollector::ClearSimpleMapTransition(Map* map, 2613 Map* dead_transition) { 2614 // A previously existing simple transition (stored in a WeakCell) is going 2615 // to be cleared. Clear the useless cell pointer, and take ownership 2616 // of the descriptor array. 2617 map->set_raw_transitions(Smi::kZero); 2618 int number_of_own_descriptors = map->NumberOfOwnDescriptors(); 2619 DescriptorArray* descriptors = map->instance_descriptors(); 2620 if (descriptors == dead_transition->instance_descriptors() && 2621 number_of_own_descriptors > 0) { 2622 TrimDescriptorArray(map, descriptors); 2623 DCHECK(descriptors->number_of_descriptors() == number_of_own_descriptors); 2624 map->set_owns_descriptors(true); 2625 } 2626 } 2627 2628 2629 void MarkCompactCollector::ClearFullMapTransitions() { 2630 HeapObject* undefined = heap()->undefined_value(); 2631 Object* obj = heap()->encountered_transition_arrays(); 2632 while (obj != Smi::kZero) { 2633 TransitionArray* array = TransitionArray::cast(obj); 2634 int num_transitions = array->number_of_entries(); 2635 DCHECK_EQ(TransitionArray::NumberOfTransitions(array), num_transitions); 2636 if (num_transitions > 0) { 2637 Map* map = array->GetTarget(0); 2638 Map* parent = Map::cast(map->constructor_or_backpointer()); 2639 bool parent_is_alive = ObjectMarking::IsBlackOrGrey(parent); 2640 DescriptorArray* descriptors = 2641 parent_is_alive ? parent->instance_descriptors() : nullptr; 2642 bool descriptors_owner_died = 2643 CompactTransitionArray(parent, array, descriptors); 2644 if (descriptors_owner_died) { 2645 TrimDescriptorArray(parent, descriptors); 2646 } 2647 } 2648 obj = array->next_link(); 2649 array->set_next_link(undefined, SKIP_WRITE_BARRIER); 2650 } 2651 heap()->set_encountered_transition_arrays(Smi::kZero); 2652 } 2653 2654 2655 bool MarkCompactCollector::CompactTransitionArray( 2656 Map* map, TransitionArray* transitions, DescriptorArray* descriptors) { 2657 int num_transitions = transitions->number_of_entries(); 2658 bool descriptors_owner_died = false; 2659 int transition_index = 0; 2660 // Compact all live transitions to the left. 2661 for (int i = 0; i < num_transitions; ++i) { 2662 Map* target = transitions->GetTarget(i); 2663 DCHECK_EQ(target->constructor_or_backpointer(), map); 2664 if (ObjectMarking::IsWhite(target)) { 2665 if (descriptors != nullptr && 2666 target->instance_descriptors() == descriptors) { 2667 descriptors_owner_died = true; 2668 } 2669 } else { 2670 if (i != transition_index) { 2671 Name* key = transitions->GetKey(i); 2672 transitions->SetKey(transition_index, key); 2673 Object** key_slot = transitions->GetKeySlot(transition_index); 2674 RecordSlot(transitions, key_slot, key); 2675 // Target slots do not need to be recorded since maps are not compacted. 2676 transitions->SetTarget(transition_index, transitions->GetTarget(i)); 2677 } 2678 transition_index++; 2679 } 2680 } 2681 // If there are no transitions to be cleared, return. 2682 if (transition_index == num_transitions) { 2683 DCHECK(!descriptors_owner_died); 2684 return false; 2685 } 2686 // Note that we never eliminate a transition array, though we might right-trim 2687 // such that number_of_transitions() == 0. If this assumption changes, 2688 // TransitionArray::Insert() will need to deal with the case that a transition 2689 // array disappeared during GC. 2690 int trim = TransitionArray::Capacity(transitions) - transition_index; 2691 if (trim > 0) { 2692 heap_->RightTrimFixedArray(transitions, 2693 trim * TransitionArray::kTransitionSize); 2694 transitions->SetNumberOfTransitions(transition_index); 2695 } 2696 return descriptors_owner_died; 2697 } 2698 2699 2700 void MarkCompactCollector::TrimDescriptorArray(Map* map, 2701 DescriptorArray* descriptors) { 2702 int number_of_own_descriptors = map->NumberOfOwnDescriptors(); 2703 if (number_of_own_descriptors == 0) { 2704 DCHECK(descriptors == heap_->empty_descriptor_array()); 2705 return; 2706 } 2707 2708 int number_of_descriptors = descriptors->number_of_descriptors_storage(); 2709 int to_trim = number_of_descriptors - number_of_own_descriptors; 2710 if (to_trim > 0) { 2711 heap_->RightTrimFixedArray(descriptors, 2712 to_trim * DescriptorArray::kEntrySize); 2713 descriptors->SetNumberOfDescriptors(number_of_own_descriptors); 2714 2715 if (descriptors->HasEnumCache()) TrimEnumCache(map, descriptors); 2716 descriptors->Sort(); 2717 2718 if (FLAG_unbox_double_fields) { 2719 LayoutDescriptor* layout_descriptor = map->layout_descriptor(); 2720 layout_descriptor = layout_descriptor->Trim(heap_, map, descriptors, 2721 number_of_own_descriptors); 2722 SLOW_DCHECK(layout_descriptor->IsConsistentWithMap(map, true)); 2723 } 2724 } 2725 DCHECK(descriptors->number_of_descriptors() == number_of_own_descriptors); 2726 map->set_owns_descriptors(true); 2727 } 2728 2729 2730 void MarkCompactCollector::TrimEnumCache(Map* map, 2731 DescriptorArray* descriptors) { 2732 int live_enum = map->EnumLength(); 2733 if (live_enum == kInvalidEnumCacheSentinel) { 2734 live_enum = 2735 map->NumberOfDescribedProperties(OWN_DESCRIPTORS, ENUMERABLE_STRINGS); 2736 } 2737 if (live_enum == 0) return descriptors->ClearEnumCache(); 2738 2739 FixedArray* enum_cache = descriptors->GetEnumCache(); 2740 2741 int to_trim = enum_cache->length() - live_enum; 2742 if (to_trim <= 0) return; 2743 heap_->RightTrimFixedArray(descriptors->GetEnumCache(), to_trim); 2744 2745 if (!descriptors->HasEnumIndicesCache()) return; 2746 FixedArray* enum_indices_cache = descriptors->GetEnumIndicesCache(); 2747 heap_->RightTrimFixedArray(enum_indices_cache, to_trim); 2748 } 2749 2750 2751 void MarkCompactCollector::ProcessWeakCollections() { 2752 Object* weak_collection_obj = heap()->encountered_weak_collections(); 2753 while (weak_collection_obj != Smi::kZero) { 2754 JSWeakCollection* weak_collection = 2755 reinterpret_cast<JSWeakCollection*>(weak_collection_obj); 2756 DCHECK(ObjectMarking::IsBlackOrGrey(weak_collection)); 2757 if (weak_collection->table()->IsHashTable()) { 2758 ObjectHashTable* table = ObjectHashTable::cast(weak_collection->table()); 2759 for (int i = 0; i < table->Capacity(); i++) { 2760 if (ObjectMarking::IsBlackOrGrey(HeapObject::cast(table->KeyAt(i)))) { 2761 Object** key_slot = 2762 table->RawFieldOfElementAt(ObjectHashTable::EntryToIndex(i)); 2763 RecordSlot(table, key_slot, *key_slot); 2764 Object** value_slot = 2765 table->RawFieldOfElementAt(ObjectHashTable::EntryToValueIndex(i)); 2766 MarkCompactMarkingVisitor::MarkObjectByPointer(this, table, 2767 value_slot); 2768 } 2769 } 2770 } 2771 weak_collection_obj = weak_collection->next(); 2772 } 2773 } 2774 2775 2776 void MarkCompactCollector::ClearWeakCollections() { 2777 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR_WEAK_COLLECTIONS); 2778 Object* weak_collection_obj = heap()->encountered_weak_collections(); 2779 while (weak_collection_obj != Smi::kZero) { 2780 JSWeakCollection* weak_collection = 2781 reinterpret_cast<JSWeakCollection*>(weak_collection_obj); 2782 DCHECK(ObjectMarking::IsBlackOrGrey(weak_collection)); 2783 if (weak_collection->table()->IsHashTable()) { 2784 ObjectHashTable* table = ObjectHashTable::cast(weak_collection->table()); 2785 for (int i = 0; i < table->Capacity(); i++) { 2786 HeapObject* key = HeapObject::cast(table->KeyAt(i)); 2787 if (!ObjectMarking::IsBlackOrGrey(key)) { 2788 table->RemoveEntry(i); 2789 } 2790 } 2791 } 2792 weak_collection_obj = weak_collection->next(); 2793 weak_collection->set_next(heap()->undefined_value()); 2794 } 2795 heap()->set_encountered_weak_collections(Smi::kZero); 2796 } 2797 2798 2799 void MarkCompactCollector::AbortWeakCollections() { 2800 Object* weak_collection_obj = heap()->encountered_weak_collections(); 2801 while (weak_collection_obj != Smi::kZero) { 2802 JSWeakCollection* weak_collection = 2803 reinterpret_cast<JSWeakCollection*>(weak_collection_obj); 2804 weak_collection_obj = weak_collection->next(); 2805 weak_collection->set_next(heap()->undefined_value()); 2806 } 2807 heap()->set_encountered_weak_collections(Smi::kZero); 2808 } 2809 2810 2811 void MarkCompactCollector::ClearWeakCells(Object** non_live_map_list, 2812 DependentCode** dependent_code_list) { 2813 Heap* heap = this->heap(); 2814 TRACE_GC(heap->tracer(), GCTracer::Scope::MC_CLEAR_WEAK_CELLS); 2815 Object* weak_cell_obj = heap->encountered_weak_cells(); 2816 Object* the_hole_value = heap->the_hole_value(); 2817 DependentCode* dependent_code_head = 2818 DependentCode::cast(heap->empty_fixed_array()); 2819 Object* non_live_map_head = Smi::kZero; 2820 while (weak_cell_obj != Smi::kZero) { 2821 WeakCell* weak_cell = reinterpret_cast<WeakCell*>(weak_cell_obj); 2822 Object* next_weak_cell = weak_cell->next(); 2823 bool clear_value = true; 2824 bool clear_next = true; 2825 // We do not insert cleared weak cells into the list, so the value 2826 // cannot be a Smi here. 2827 HeapObject* value = HeapObject::cast(weak_cell->value()); 2828 if (!ObjectMarking::IsBlackOrGrey(value)) { 2829 // Cells for new-space objects embedded in optimized code are wrapped in 2830 // WeakCell and put into Heap::weak_object_to_code_table. 2831 // Such cells do not have any strong references but we want to keep them 2832 // alive as long as the cell value is alive. 2833 // TODO(ulan): remove this once we remove Heap::weak_object_to_code_table. 2834 if (value->IsCell()) { 2835 Object* cell_value = Cell::cast(value)->value(); 2836 if (cell_value->IsHeapObject() && 2837 ObjectMarking::IsBlackOrGrey(HeapObject::cast(cell_value))) { 2838 // Resurrect the cell. 2839 ObjectMarking::WhiteToBlack(value); 2840 Object** slot = HeapObject::RawField(value, Cell::kValueOffset); 2841 RecordSlot(value, slot, *slot); 2842 slot = HeapObject::RawField(weak_cell, WeakCell::kValueOffset); 2843 RecordSlot(weak_cell, slot, *slot); 2844 clear_value = false; 2845 } 2846 } 2847 if (value->IsMap()) { 2848 // The map is non-live. 2849 Map* map = Map::cast(value); 2850 // Add dependent code to the dependent_code_list. 2851 DependentCode* candidate = map->dependent_code(); 2852 // We rely on the fact that the weak code group comes first. 2853 STATIC_ASSERT(DependentCode::kWeakCodeGroup == 0); 2854 if (candidate->length() > 0 && 2855 candidate->group() == DependentCode::kWeakCodeGroup) { 2856 candidate->set_next_link(dependent_code_head); 2857 dependent_code_head = candidate; 2858 } 2859 // Add the weak cell to the non_live_map list. 2860 weak_cell->set_next(non_live_map_head); 2861 non_live_map_head = weak_cell; 2862 clear_value = false; 2863 clear_next = false; 2864 } 2865 } else { 2866 // The value of the weak cell is alive. 2867 Object** slot = HeapObject::RawField(weak_cell, WeakCell::kValueOffset); 2868 RecordSlot(weak_cell, slot, *slot); 2869 clear_value = false; 2870 } 2871 if (clear_value) { 2872 weak_cell->clear(); 2873 } 2874 if (clear_next) { 2875 weak_cell->clear_next(the_hole_value); 2876 } 2877 weak_cell_obj = next_weak_cell; 2878 } 2879 heap->set_encountered_weak_cells(Smi::kZero); 2880 *non_live_map_list = non_live_map_head; 2881 *dependent_code_list = dependent_code_head; 2882 } 2883 2884 2885 void MarkCompactCollector::AbortWeakCells() { 2886 Object* the_hole_value = heap()->the_hole_value(); 2887 Object* weak_cell_obj = heap()->encountered_weak_cells(); 2888 while (weak_cell_obj != Smi::kZero) { 2889 WeakCell* weak_cell = reinterpret_cast<WeakCell*>(weak_cell_obj); 2890 weak_cell_obj = weak_cell->next(); 2891 weak_cell->clear_next(the_hole_value); 2892 } 2893 heap()->set_encountered_weak_cells(Smi::kZero); 2894 } 2895 2896 2897 void MarkCompactCollector::AbortTransitionArrays() { 2898 HeapObject* undefined = heap()->undefined_value(); 2899 Object* obj = heap()->encountered_transition_arrays(); 2900 while (obj != Smi::kZero) { 2901 TransitionArray* array = TransitionArray::cast(obj); 2902 obj = array->next_link(); 2903 array->set_next_link(undefined, SKIP_WRITE_BARRIER); 2904 } 2905 heap()->set_encountered_transition_arrays(Smi::kZero); 2906 } 2907 2908 void MarkCompactCollector::RecordRelocSlot(Code* host, RelocInfo* rinfo, 2909 Object* target) { 2910 Page* target_page = Page::FromAddress(reinterpret_cast<Address>(target)); 2911 Page* source_page = Page::FromAddress(reinterpret_cast<Address>(host)); 2912 if (target_page->IsEvacuationCandidate() && 2913 (rinfo->host() == NULL || 2914 !ShouldSkipEvacuationSlotRecording(rinfo->host()))) { 2915 RelocInfo::Mode rmode = rinfo->rmode(); 2916 Address addr = rinfo->pc(); 2917 SlotType slot_type = SlotTypeForRelocInfoMode(rmode); 2918 if (rinfo->IsInConstantPool()) { 2919 addr = rinfo->constant_pool_entry_address(); 2920 if (RelocInfo::IsCodeTarget(rmode)) { 2921 slot_type = CODE_ENTRY_SLOT; 2922 } else { 2923 DCHECK(RelocInfo::IsEmbeddedObject(rmode)); 2924 slot_type = OBJECT_SLOT; 2925 } 2926 } 2927 RememberedSet<OLD_TO_OLD>::InsertTyped( 2928 source_page, reinterpret_cast<Address>(host), slot_type, addr); 2929 } 2930 } 2931 2932 static inline SlotCallbackResult UpdateSlot(Object** slot) { 2933 Object* obj = reinterpret_cast<Object*>( 2934 base::NoBarrier_Load(reinterpret_cast<base::AtomicWord*>(slot))); 2935 2936 if (obj->IsHeapObject()) { 2937 HeapObject* heap_obj = HeapObject::cast(obj); 2938 MapWord map_word = heap_obj->map_word(); 2939 if (map_word.IsForwardingAddress()) { 2940 DCHECK(heap_obj->GetHeap()->InFromSpace(heap_obj) || 2941 MarkCompactCollector::IsOnEvacuationCandidate(heap_obj) || 2942 Page::FromAddress(heap_obj->address()) 2943 ->IsFlagSet(Page::COMPACTION_WAS_ABORTED)); 2944 HeapObject* target = map_word.ToForwardingAddress(); 2945 base::NoBarrier_CompareAndSwap( 2946 reinterpret_cast<base::AtomicWord*>(slot), 2947 reinterpret_cast<base::AtomicWord>(obj), 2948 reinterpret_cast<base::AtomicWord>(target)); 2949 DCHECK(!heap_obj->GetHeap()->InFromSpace(target) && 2950 !MarkCompactCollector::IsOnEvacuationCandidate(target)); 2951 } 2952 } 2953 return REMOVE_SLOT; 2954 } 2955 2956 // Visitor for updating pointers from live objects in old spaces to new space. 2957 // It does not expect to encounter pointers to dead objects. 2958 class PointersUpdatingVisitor : public ObjectVisitor { 2959 public: 2960 void VisitPointer(Object** p) override { UpdateSlot(p); } 2961 2962 void VisitPointers(Object** start, Object** end) override { 2963 for (Object** p = start; p < end; p++) UpdateSlot(p); 2964 } 2965 2966 void VisitCell(RelocInfo* rinfo) override { 2967 UpdateTypedSlotHelper::UpdateCell(rinfo, UpdateSlot); 2968 } 2969 2970 void VisitEmbeddedPointer(RelocInfo* rinfo) override { 2971 UpdateTypedSlotHelper::UpdateEmbeddedPointer(rinfo, UpdateSlot); 2972 } 2973 2974 void VisitCodeTarget(RelocInfo* rinfo) override { 2975 UpdateTypedSlotHelper::UpdateCodeTarget(rinfo, UpdateSlot); 2976 } 2977 2978 void VisitCodeEntry(Address entry_address) override { 2979 UpdateTypedSlotHelper::UpdateCodeEntry(entry_address, UpdateSlot); 2980 } 2981 2982 void VisitDebugTarget(RelocInfo* rinfo) override { 2983 UpdateTypedSlotHelper::UpdateDebugTarget(rinfo, UpdateSlot); 2984 } 2985 }; 2986 2987 static String* UpdateReferenceInExternalStringTableEntry(Heap* heap, 2988 Object** p) { 2989 MapWord map_word = HeapObject::cast(*p)->map_word(); 2990 2991 if (map_word.IsForwardingAddress()) { 2992 return String::cast(map_word.ToForwardingAddress()); 2993 } 2994 2995 return String::cast(*p); 2996 } 2997 2998 void MarkCompactCollector::EvacuatePrologue() { 2999 // New space. 3000 NewSpace* new_space = heap()->new_space(); 3001 // Append the list of new space pages to be processed. 3002 for (Page* p : PageRange(new_space->bottom(), new_space->top())) { 3003 new_space_evacuation_pages_.Add(p); 3004 } 3005 new_space->Flip(); 3006 new_space->ResetAllocationInfo(); 3007 3008 // Old space. 3009 CHECK(old_space_evacuation_pages_.is_empty()); 3010 old_space_evacuation_pages_.Swap(&evacuation_candidates_); 3011 } 3012 3013 void MarkCompactCollector::EvacuateEpilogue() { 3014 // New space. 3015 heap()->new_space()->set_age_mark(heap()->new_space()->top()); 3016 // Old space. Deallocate evacuated candidate pages. 3017 ReleaseEvacuationCandidates(); 3018 } 3019 3020 class MarkCompactCollector::Evacuator : public Malloced { 3021 public: 3022 enum EvacuationMode { 3023 kObjectsNewToOld, 3024 kPageNewToOld, 3025 kObjectsOldToOld, 3026 kPageNewToNew, 3027 }; 3028 3029 static inline EvacuationMode ComputeEvacuationMode(MemoryChunk* chunk) { 3030 // Note: The order of checks is important in this function. 3031 if (chunk->IsFlagSet(MemoryChunk::PAGE_NEW_OLD_PROMOTION)) 3032 return kPageNewToOld; 3033 if (chunk->IsFlagSet(MemoryChunk::PAGE_NEW_NEW_PROMOTION)) 3034 return kPageNewToNew; 3035 if (chunk->InNewSpace()) return kObjectsNewToOld; 3036 DCHECK(chunk->IsEvacuationCandidate()); 3037 return kObjectsOldToOld; 3038 } 3039 3040 // NewSpacePages with more live bytes than this threshold qualify for fast 3041 // evacuation. 3042 static int PageEvacuationThreshold() { 3043 if (FLAG_page_promotion) 3044 return FLAG_page_promotion_threshold * Page::kAllocatableMemory / 100; 3045 return Page::kAllocatableMemory + kPointerSize; 3046 } 3047 3048 explicit Evacuator(MarkCompactCollector* collector) 3049 : collector_(collector), 3050 compaction_spaces_(collector->heap()), 3051 local_pretenuring_feedback_(kInitialLocalPretenuringFeedbackCapacity), 3052 new_space_visitor_(collector->heap(), &compaction_spaces_, 3053 &local_pretenuring_feedback_), 3054 new_to_new_page_visitor_(collector->heap(), 3055 &local_pretenuring_feedback_), 3056 new_to_old_page_visitor_(collector->heap(), 3057 &local_pretenuring_feedback_), 3058 3059 old_space_visitor_(collector->heap(), &compaction_spaces_), 3060 duration_(0.0), 3061 bytes_compacted_(0) {} 3062 3063 inline bool EvacuatePage(Page* chunk); 3064 3065 // Merge back locally cached info sequentially. Note that this method needs 3066 // to be called from the main thread. 3067 inline void Finalize(); 3068 3069 CompactionSpaceCollection* compaction_spaces() { return &compaction_spaces_; } 3070 3071 private: 3072 static const int kInitialLocalPretenuringFeedbackCapacity = 256; 3073 3074 inline Heap* heap() { return collector_->heap(); } 3075 3076 void ReportCompactionProgress(double duration, intptr_t bytes_compacted) { 3077 duration_ += duration; 3078 bytes_compacted_ += bytes_compacted; 3079 } 3080 3081 MarkCompactCollector* collector_; 3082 3083 // Locally cached collector data. 3084 CompactionSpaceCollection compaction_spaces_; 3085 base::HashMap local_pretenuring_feedback_; 3086 3087 // Visitors for the corresponding spaces. 3088 EvacuateNewSpaceVisitor new_space_visitor_; 3089 EvacuateNewSpacePageVisitor<PageEvacuationMode::NEW_TO_NEW> 3090 new_to_new_page_visitor_; 3091 EvacuateNewSpacePageVisitor<PageEvacuationMode::NEW_TO_OLD> 3092 new_to_old_page_visitor_; 3093 EvacuateOldSpaceVisitor old_space_visitor_; 3094 3095 // Book keeping info. 3096 double duration_; 3097 intptr_t bytes_compacted_; 3098 }; 3099 3100 bool MarkCompactCollector::Evacuator::EvacuatePage(Page* page) { 3101 bool success = false; 3102 DCHECK(page->SweepingDone()); 3103 int saved_live_bytes = page->LiveBytes(); 3104 double evacuation_time = 0.0; 3105 Heap* heap = page->heap(); 3106 { 3107 AlwaysAllocateScope always_allocate(heap->isolate()); 3108 TimedScope timed_scope(&evacuation_time); 3109 switch (ComputeEvacuationMode(page)) { 3110 case kObjectsNewToOld: 3111 success = collector_->VisitLiveObjects(page, &new_space_visitor_, 3112 kClearMarkbits); 3113 DCHECK(success); 3114 ArrayBufferTracker::ProcessBuffers( 3115 page, ArrayBufferTracker::kUpdateForwardedRemoveOthers); 3116 break; 3117 case kPageNewToOld: 3118 success = collector_->VisitLiveObjects(page, &new_to_old_page_visitor_, 3119 kKeepMarking); 3120 DCHECK(success); 3121 new_to_old_page_visitor_.account_moved_bytes(page->LiveBytes()); 3122 // ArrayBufferTracker will be updated during sweeping. 3123 break; 3124 case kPageNewToNew: 3125 success = collector_->VisitLiveObjects(page, &new_to_new_page_visitor_, 3126 kKeepMarking); 3127 DCHECK(success); 3128 new_to_new_page_visitor_.account_moved_bytes(page->LiveBytes()); 3129 // ArrayBufferTracker will be updated during sweeping. 3130 break; 3131 case kObjectsOldToOld: 3132 success = collector_->VisitLiveObjects(page, &old_space_visitor_, 3133 kClearMarkbits); 3134 if (!success) { 3135 // Aborted compaction page. We have to record slots here, since we 3136 // might not have recorded them in first place. 3137 // Note: We mark the page as aborted here to be able to record slots 3138 // for code objects in |RecordMigratedSlotVisitor|. 3139 page->SetFlag(Page::COMPACTION_WAS_ABORTED); 3140 EvacuateRecordOnlyVisitor record_visitor(collector_->heap()); 3141 success = 3142 collector_->VisitLiveObjects(page, &record_visitor, kKeepMarking); 3143 ArrayBufferTracker::ProcessBuffers( 3144 page, ArrayBufferTracker::kUpdateForwardedKeepOthers); 3145 DCHECK(success); 3146 // We need to return failure here to indicate that we want this page 3147 // added to the sweeper. 3148 success = false; 3149 } else { 3150 ArrayBufferTracker::ProcessBuffers( 3151 page, ArrayBufferTracker::kUpdateForwardedRemoveOthers); 3152 } 3153 break; 3154 } 3155 } 3156 ReportCompactionProgress(evacuation_time, saved_live_bytes); 3157 if (FLAG_trace_evacuation) { 3158 PrintIsolate(heap->isolate(), 3159 "evacuation[%p]: page=%p new_space=%d " 3160 "page_evacuation=%d executable=%d contains_age_mark=%d " 3161 "live_bytes=%d time=%f\n", 3162 static_cast<void*>(this), static_cast<void*>(page), 3163 page->InNewSpace(), 3164 page->IsFlagSet(Page::PAGE_NEW_OLD_PROMOTION) || 3165 page->IsFlagSet(Page::PAGE_NEW_NEW_PROMOTION), 3166 page->IsFlagSet(MemoryChunk::IS_EXECUTABLE), 3167 page->Contains(heap->new_space()->age_mark()), 3168 saved_live_bytes, evacuation_time); 3169 } 3170 return success; 3171 } 3172 3173 void MarkCompactCollector::Evacuator::Finalize() { 3174 heap()->old_space()->MergeCompactionSpace(compaction_spaces_.Get(OLD_SPACE)); 3175 heap()->code_space()->MergeCompactionSpace( 3176 compaction_spaces_.Get(CODE_SPACE)); 3177 heap()->tracer()->AddCompactionEvent(duration_, bytes_compacted_); 3178 heap()->IncrementPromotedObjectsSize(new_space_visitor_.promoted_size() + 3179 new_to_old_page_visitor_.moved_bytes()); 3180 heap()->IncrementSemiSpaceCopiedObjectSize( 3181 new_space_visitor_.semispace_copied_size() + 3182 new_to_new_page_visitor_.moved_bytes()); 3183 heap()->IncrementYoungSurvivorsCounter( 3184 new_space_visitor_.promoted_size() + 3185 new_space_visitor_.semispace_copied_size() + 3186 new_to_old_page_visitor_.moved_bytes() + 3187 new_to_new_page_visitor_.moved_bytes()); 3188 heap()->MergeAllocationSitePretenuringFeedback(local_pretenuring_feedback_); 3189 } 3190 3191 int MarkCompactCollector::NumberOfParallelCompactionTasks(int pages, 3192 intptr_t live_bytes) { 3193 if (!FLAG_parallel_compaction) return 1; 3194 // Compute the number of needed tasks based on a target compaction time, the 3195 // profiled compaction speed and marked live memory. 3196 // 3197 // The number of parallel compaction tasks is limited by: 3198 // - #evacuation pages 3199 // - #cores 3200 const double kTargetCompactionTimeInMs = .5; 3201 3202 double compaction_speed = 3203 heap()->tracer()->CompactionSpeedInBytesPerMillisecond(); 3204 3205 const int available_cores = Max( 3206 1, static_cast<int>( 3207 V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads())); 3208 int tasks; 3209 if (compaction_speed > 0) { 3210 tasks = 1 + static_cast<int>(live_bytes / compaction_speed / 3211 kTargetCompactionTimeInMs); 3212 } else { 3213 tasks = pages; 3214 } 3215 const int tasks_capped_pages = Min(pages, tasks); 3216 return Min(available_cores, tasks_capped_pages); 3217 } 3218 3219 class EvacuationJobTraits { 3220 public: 3221 typedef int* PerPageData; // Pointer to number of aborted pages. 3222 typedef MarkCompactCollector::Evacuator* PerTaskData; 3223 3224 static const bool NeedSequentialFinalization = true; 3225 3226 static bool ProcessPageInParallel(Heap* heap, PerTaskData evacuator, 3227 MemoryChunk* chunk, PerPageData) { 3228 return evacuator->EvacuatePage(reinterpret_cast<Page*>(chunk)); 3229 } 3230 3231 static void FinalizePageSequentially(Heap* heap, MemoryChunk* chunk, 3232 bool success, PerPageData data) { 3233 using Evacuator = MarkCompactCollector::Evacuator; 3234 Page* p = static_cast<Page*>(chunk); 3235 switch (Evacuator::ComputeEvacuationMode(p)) { 3236 case Evacuator::kPageNewToOld: 3237 break; 3238 case Evacuator::kPageNewToNew: 3239 DCHECK(success); 3240 break; 3241 case Evacuator::kObjectsNewToOld: 3242 DCHECK(success); 3243 break; 3244 case Evacuator::kObjectsOldToOld: 3245 if (success) { 3246 DCHECK(p->IsEvacuationCandidate()); 3247 DCHECK(p->SweepingDone()); 3248 p->Unlink(); 3249 } else { 3250 // We have partially compacted the page, i.e., some objects may have 3251 // moved, others are still in place. 3252 p->ClearEvacuationCandidate(); 3253 // Slots have already been recorded so we just need to add it to the 3254 // sweeper, which will happen after updating pointers. 3255 *data += 1; 3256 } 3257 break; 3258 default: 3259 UNREACHABLE(); 3260 } 3261 } 3262 }; 3263 3264 void MarkCompactCollector::EvacuatePagesInParallel() { 3265 PageParallelJob<EvacuationJobTraits> job( 3266 heap_, heap_->isolate()->cancelable_task_manager(), 3267 &page_parallel_job_semaphore_); 3268 3269 int abandoned_pages = 0; 3270 intptr_t live_bytes = 0; 3271 for (Page* page : old_space_evacuation_pages_) { 3272 live_bytes += page->LiveBytes(); 3273 job.AddPage(page, &abandoned_pages); 3274 } 3275 3276 const bool reduce_memory = heap()->ShouldReduceMemory(); 3277 const Address age_mark = heap()->new_space()->age_mark(); 3278 for (Page* page : new_space_evacuation_pages_) { 3279 live_bytes += page->LiveBytes(); 3280 if (!reduce_memory && !page->NeverEvacuate() && 3281 (page->LiveBytes() > Evacuator::PageEvacuationThreshold()) && 3282 !page->Contains(age_mark) && 3283 heap()->CanExpandOldGeneration(page->LiveBytes())) { 3284 if (page->IsFlagSet(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK)) { 3285 EvacuateNewSpacePageVisitor<NEW_TO_OLD>::Move(page); 3286 } else { 3287 EvacuateNewSpacePageVisitor<NEW_TO_NEW>::Move(page); 3288 } 3289 } 3290 3291 job.AddPage(page, &abandoned_pages); 3292 } 3293 DCHECK_GE(job.NumberOfPages(), 1); 3294 3295 // Used for trace summary. 3296 double compaction_speed = 0; 3297 if (FLAG_trace_evacuation) { 3298 compaction_speed = heap()->tracer()->CompactionSpeedInBytesPerMillisecond(); 3299 } 3300 3301 const int wanted_num_tasks = 3302 NumberOfParallelCompactionTasks(job.NumberOfPages(), live_bytes); 3303 Evacuator** evacuators = new Evacuator*[wanted_num_tasks]; 3304 for (int i = 0; i < wanted_num_tasks; i++) { 3305 evacuators[i] = new Evacuator(this); 3306 } 3307 job.Run(wanted_num_tasks, [evacuators](int i) { return evacuators[i]; }); 3308 for (int i = 0; i < wanted_num_tasks; i++) { 3309 evacuators[i]->Finalize(); 3310 delete evacuators[i]; 3311 } 3312 delete[] evacuators; 3313 3314 if (FLAG_trace_evacuation) { 3315 PrintIsolate(isolate(), 3316 "%8.0f ms: evacuation-summary: parallel=%s pages=%d " 3317 "aborted=%d wanted_tasks=%d tasks=%d cores=%" PRIuS 3318 " live_bytes=%" V8PRIdPTR " compaction_speed=%.f\n", 3319 isolate()->time_millis_since_init(), 3320 FLAG_parallel_compaction ? "yes" : "no", job.NumberOfPages(), 3321 abandoned_pages, wanted_num_tasks, job.NumberOfTasks(), 3322 V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads(), 3323 live_bytes, compaction_speed); 3324 } 3325 } 3326 3327 class EvacuationWeakObjectRetainer : public WeakObjectRetainer { 3328 public: 3329 virtual Object* RetainAs(Object* object) { 3330 if (object->IsHeapObject()) { 3331 HeapObject* heap_object = HeapObject::cast(object); 3332 MapWord map_word = heap_object->map_word(); 3333 if (map_word.IsForwardingAddress()) { 3334 return map_word.ToForwardingAddress(); 3335 } 3336 } 3337 return object; 3338 } 3339 }; 3340 3341 MarkCompactCollector::Sweeper::ClearOldToNewSlotsMode 3342 MarkCompactCollector::Sweeper::GetClearOldToNewSlotsMode(Page* p) { 3343 AllocationSpace identity = p->owner()->identity(); 3344 if (p->old_to_new_slots() && 3345 (identity == OLD_SPACE || identity == MAP_SPACE)) { 3346 return MarkCompactCollector::Sweeper::CLEAR_REGULAR_SLOTS; 3347 } else if (p->typed_old_to_new_slots() && identity == CODE_SPACE) { 3348 return MarkCompactCollector::Sweeper::CLEAR_TYPED_SLOTS; 3349 } 3350 return MarkCompactCollector::Sweeper::DO_NOT_CLEAR; 3351 } 3352 3353 int MarkCompactCollector::Sweeper::RawSweep( 3354 Page* p, FreeListRebuildingMode free_list_mode, 3355 FreeSpaceTreatmentMode free_space_mode) { 3356 Space* space = p->owner(); 3357 DCHECK_NOT_NULL(space); 3358 DCHECK(free_list_mode == IGNORE_FREE_LIST || space->identity() == OLD_SPACE || 3359 space->identity() == CODE_SPACE || space->identity() == MAP_SPACE); 3360 DCHECK(!p->IsEvacuationCandidate() && !p->SweepingDone()); 3361 3362 // If there are old-to-new slots in that page, we have to filter out slots 3363 // that are in dead memory which is freed by the sweeper. 3364 ClearOldToNewSlotsMode slots_clearing_mode = GetClearOldToNewSlotsMode(p); 3365 3366 // The free ranges map is used for filtering typed slots. 3367 std::map<uint32_t, uint32_t> free_ranges; 3368 3369 // Before we sweep objects on the page, we free dead array buffers which 3370 // requires valid mark bits. 3371 ArrayBufferTracker::FreeDead(p); 3372 3373 Address free_start = p->area_start(); 3374 DCHECK(reinterpret_cast<intptr_t>(free_start) % (32 * kPointerSize) == 0); 3375 3376 // If we use the skip list for code space pages, we have to lock the skip 3377 // list because it could be accessed concurrently by the runtime or the 3378 // deoptimizer. 3379 const bool rebuild_skip_list = 3380 space->identity() == CODE_SPACE && p->skip_list() != nullptr; 3381 SkipList* skip_list = p->skip_list(); 3382 if (rebuild_skip_list) { 3383 skip_list->Clear(); 3384 } 3385 3386 intptr_t freed_bytes = 0; 3387 intptr_t max_freed_bytes = 0; 3388 int curr_region = -1; 3389 3390 LiveObjectIterator<kBlackObjects> it(p); 3391 HeapObject* object = NULL; 3392 3393 while ((object = it.Next()) != NULL) { 3394 DCHECK(ObjectMarking::IsBlack(object)); 3395 Address free_end = object->address(); 3396 if (free_end != free_start) { 3397 CHECK_GT(free_end, free_start); 3398 size_t size = static_cast<size_t>(free_end - free_start); 3399 if (free_space_mode == ZAP_FREE_SPACE) { 3400 memset(free_start, 0xcc, size); 3401 } 3402 if (free_list_mode == REBUILD_FREE_LIST) { 3403 freed_bytes = reinterpret_cast<PagedSpace*>(space)->UnaccountedFree( 3404 free_start, size); 3405 max_freed_bytes = Max(freed_bytes, max_freed_bytes); 3406 } else { 3407 p->heap()->CreateFillerObjectAt(free_start, static_cast<int>(size), 3408 ClearRecordedSlots::kNo); 3409 } 3410 3411 if (slots_clearing_mode == CLEAR_REGULAR_SLOTS) { 3412 RememberedSet<OLD_TO_NEW>::RemoveRange(p, free_start, free_end, 3413 SlotSet::KEEP_EMPTY_BUCKETS); 3414 } else if (slots_clearing_mode == CLEAR_TYPED_SLOTS) { 3415 free_ranges.insert(std::pair<uint32_t, uint32_t>( 3416 static_cast<uint32_t>(free_start - p->address()), 3417 static_cast<uint32_t>(free_end - p->address()))); 3418 } 3419 } 3420 Map* map = object->synchronized_map(); 3421 int size = object->SizeFromMap(map); 3422 if (rebuild_skip_list) { 3423 int new_region_start = SkipList::RegionNumber(free_end); 3424 int new_region_end = 3425 SkipList::RegionNumber(free_end + size - kPointerSize); 3426 if (new_region_start != curr_region || new_region_end != curr_region) { 3427 skip_list->AddObject(free_end, size); 3428 curr_region = new_region_end; 3429 } 3430 } 3431 free_start = free_end + size; 3432 } 3433 3434 if (free_start != p->area_end()) { 3435 CHECK_GT(p->area_end(), free_start); 3436 size_t size = static_cast<size_t>(p->area_end() - free_start); 3437 if (free_space_mode == ZAP_FREE_SPACE) { 3438 memset(free_start, 0xcc, size); 3439 } 3440 if (free_list_mode == REBUILD_FREE_LIST) { 3441 freed_bytes = reinterpret_cast<PagedSpace*>(space)->UnaccountedFree( 3442 free_start, size); 3443 max_freed_bytes = Max(freed_bytes, max_freed_bytes); 3444 } else { 3445 p->heap()->CreateFillerObjectAt(free_start, static_cast<int>(size), 3446 ClearRecordedSlots::kNo); 3447 } 3448 3449 if (slots_clearing_mode == CLEAR_REGULAR_SLOTS) { 3450 RememberedSet<OLD_TO_NEW>::RemoveRange(p, free_start, p->area_end(), 3451 SlotSet::KEEP_EMPTY_BUCKETS); 3452 } else if (slots_clearing_mode == CLEAR_TYPED_SLOTS) { 3453 free_ranges.insert(std::pair<uint32_t, uint32_t>( 3454 static_cast<uint32_t>(free_start - p->address()), 3455 static_cast<uint32_t>(p->area_end() - p->address()))); 3456 } 3457 } 3458 3459 // Clear invalid typed slots after collection all free ranges. 3460 if (slots_clearing_mode == CLEAR_TYPED_SLOTS) { 3461 p->typed_old_to_new_slots()->RemoveInvaldSlots(free_ranges); 3462 } 3463 3464 // Clear the mark bits of that page and reset live bytes count. 3465 p->ClearLiveness(); 3466 3467 p->concurrent_sweeping_state().SetValue(Page::kSweepingDone); 3468 if (free_list_mode == IGNORE_FREE_LIST) return 0; 3469 return static_cast<int>(FreeList::GuaranteedAllocatable(max_freed_bytes)); 3470 } 3471 3472 void MarkCompactCollector::InvalidateCode(Code* code) { 3473 Page* page = Page::FromAddress(code->address()); 3474 Address start = code->instruction_start(); 3475 Address end = code->address() + code->Size(); 3476 3477 RememberedSet<OLD_TO_NEW>::RemoveRangeTyped(page, start, end); 3478 3479 if (heap_->incremental_marking()->IsCompacting() && 3480 !ShouldSkipEvacuationSlotRecording(code)) { 3481 DCHECK(compacting_); 3482 3483 // If the object is white than no slots were recorded on it yet. 3484 if (ObjectMarking::IsWhite(code)) return; 3485 3486 // Ignore all slots that might have been recorded in the body of the 3487 // deoptimized code object. Assumption: no slots will be recorded for 3488 // this object after invalidating it. 3489 RememberedSet<OLD_TO_OLD>::RemoveRangeTyped(page, start, end); 3490 } 3491 } 3492 3493 3494 // Return true if the given code is deoptimized or will be deoptimized. 3495 bool MarkCompactCollector::WillBeDeoptimized(Code* code) { 3496 return code->is_optimized_code() && code->marked_for_deoptimization(); 3497 } 3498 3499 3500 #ifdef VERIFY_HEAP 3501 static void VerifyAllBlackObjects(MemoryChunk* page) { 3502 LiveObjectIterator<kAllLiveObjects> it(page); 3503 HeapObject* object = NULL; 3504 while ((object = it.Next()) != NULL) { 3505 CHECK(ObjectMarking::IsBlack(object)); 3506 } 3507 } 3508 #endif // VERIFY_HEAP 3509 3510 void MarkCompactCollector::RecordLiveSlotsOnPage(Page* page) { 3511 EvacuateRecordOnlyVisitor visitor(heap()); 3512 VisitLiveObjects(page, &visitor, kKeepMarking); 3513 } 3514 3515 template <class Visitor> 3516 bool MarkCompactCollector::VisitLiveObjects(MemoryChunk* page, Visitor* visitor, 3517 IterationMode mode) { 3518 #ifdef VERIFY_HEAP 3519 VerifyAllBlackObjects(page); 3520 #endif // VERIFY_HEAP 3521 3522 LiveObjectIterator<kBlackObjects> it(page); 3523 HeapObject* object = nullptr; 3524 while ((object = it.Next()) != nullptr) { 3525 DCHECK(ObjectMarking::IsBlack(object)); 3526 if (!visitor->Visit(object)) { 3527 if (mode == kClearMarkbits) { 3528 page->markbits()->ClearRange( 3529 page->AddressToMarkbitIndex(page->area_start()), 3530 page->AddressToMarkbitIndex(object->address())); 3531 if (page->old_to_new_slots() != nullptr) { 3532 page->old_to_new_slots()->RemoveRange( 3533 0, static_cast<int>(object->address() - page->address()), 3534 SlotSet::PREFREE_EMPTY_BUCKETS); 3535 } 3536 if (page->typed_old_to_new_slots() != nullptr) { 3537 RememberedSet<OLD_TO_NEW>::RemoveRangeTyped(page, page->address(), 3538 object->address()); 3539 } 3540 RecomputeLiveBytes(page); 3541 } 3542 return false; 3543 } 3544 } 3545 if (mode == kClearMarkbits) { 3546 page->ClearLiveness(); 3547 } 3548 return true; 3549 } 3550 3551 3552 void MarkCompactCollector::RecomputeLiveBytes(MemoryChunk* page) { 3553 LiveObjectIterator<kBlackObjects> it(page); 3554 int new_live_size = 0; 3555 HeapObject* object = nullptr; 3556 while ((object = it.Next()) != nullptr) { 3557 new_live_size += object->Size(); 3558 } 3559 page->SetLiveBytes(new_live_size); 3560 } 3561 3562 void MarkCompactCollector::Sweeper::AddSweptPageSafe(PagedSpace* space, 3563 Page* page) { 3564 base::LockGuard<base::Mutex> guard(&mutex_); 3565 swept_list_[space->identity()].Add(page); 3566 } 3567 3568 void MarkCompactCollector::EvacuateNewSpaceAndCandidates() { 3569 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_EVACUATE); 3570 Heap::RelocationLock relocation_lock(heap()); 3571 3572 { 3573 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_EVACUATE_PROLOGUE); 3574 EvacuatePrologue(); 3575 } 3576 3577 { 3578 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_EVACUATE_COPY); 3579 EvacuationScope evacuation_scope(this); 3580 EvacuatePagesInParallel(); 3581 } 3582 3583 UpdatePointersAfterEvacuation(); 3584 3585 { 3586 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_EVACUATE_REBALANCE); 3587 if (!heap()->new_space()->Rebalance()) { 3588 FatalProcessOutOfMemory("NewSpace::Rebalance"); 3589 } 3590 } 3591 3592 // Give pages that are queued to be freed back to the OS. Note that filtering 3593 // slots only handles old space (for unboxed doubles), and thus map space can 3594 // still contain stale pointers. We only free the chunks after pointer updates 3595 // to still have access to page headers. 3596 heap()->memory_allocator()->unmapper()->FreeQueuedChunks(); 3597 3598 { 3599 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_EVACUATE_CLEAN_UP); 3600 3601 for (Page* p : new_space_evacuation_pages_) { 3602 if (p->IsFlagSet(Page::PAGE_NEW_NEW_PROMOTION)) { 3603 p->ClearFlag(Page::PAGE_NEW_NEW_PROMOTION); 3604 sweeper().AddPage(p->owner()->identity(), p); 3605 } else if (p->IsFlagSet(Page::PAGE_NEW_OLD_PROMOTION)) { 3606 p->ClearFlag(Page::PAGE_NEW_OLD_PROMOTION); 3607 p->ForAllFreeListCategories( 3608 [](FreeListCategory* category) { DCHECK(!category->is_linked()); }); 3609 sweeper().AddPage(p->owner()->identity(), p); 3610 } 3611 } 3612 new_space_evacuation_pages_.Rewind(0); 3613 3614 for (Page* p : old_space_evacuation_pages_) { 3615 // Important: skip list should be cleared only after roots were updated 3616 // because root iteration traverses the stack and might have to find 3617 // code objects from non-updated pc pointing into evacuation candidate. 3618 SkipList* list = p->skip_list(); 3619 if (list != NULL) list->Clear(); 3620 if (p->IsFlagSet(Page::COMPACTION_WAS_ABORTED)) { 3621 sweeper().AddPage(p->owner()->identity(), p); 3622 p->ClearFlag(Page::COMPACTION_WAS_ABORTED); 3623 } 3624 } 3625 } 3626 3627 { 3628 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_EVACUATE_EPILOGUE); 3629 EvacuateEpilogue(); 3630 } 3631 3632 #ifdef VERIFY_HEAP 3633 if (FLAG_verify_heap && !sweeper().sweeping_in_progress()) { 3634 VerifyEvacuation(heap()); 3635 } 3636 #endif 3637 } 3638 3639 template <PointerDirection direction> 3640 class PointerUpdateJobTraits { 3641 public: 3642 typedef int PerPageData; // Per page data is not used in this job. 3643 typedef int PerTaskData; // Per task data is not used in this job. 3644 3645 static bool ProcessPageInParallel(Heap* heap, PerTaskData, MemoryChunk* chunk, 3646 PerPageData) { 3647 UpdateUntypedPointers(heap, chunk); 3648 UpdateTypedPointers(heap, chunk); 3649 return true; 3650 } 3651 static const bool NeedSequentialFinalization = false; 3652 static void FinalizePageSequentially(Heap*, MemoryChunk*, bool, PerPageData) { 3653 } 3654 3655 private: 3656 static void UpdateUntypedPointers(Heap* heap, MemoryChunk* chunk) { 3657 if (direction == OLD_TO_NEW) { 3658 RememberedSet<OLD_TO_NEW>::Iterate(chunk, [heap](Address slot) { 3659 return CheckAndUpdateOldToNewSlot(heap, slot); 3660 }); 3661 } else { 3662 RememberedSet<OLD_TO_OLD>::Iterate(chunk, [](Address slot) { 3663 return UpdateSlot(reinterpret_cast<Object**>(slot)); 3664 }); 3665 } 3666 } 3667 3668 static void UpdateTypedPointers(Heap* heap, MemoryChunk* chunk) { 3669 if (direction == OLD_TO_OLD) { 3670 Isolate* isolate = heap->isolate(); 3671 RememberedSet<OLD_TO_OLD>::IterateTyped( 3672 chunk, [isolate](SlotType type, Address host_addr, Address slot) { 3673 return UpdateTypedSlotHelper::UpdateTypedSlot(isolate, type, slot, 3674 UpdateSlot); 3675 }); 3676 } else { 3677 Isolate* isolate = heap->isolate(); 3678 RememberedSet<OLD_TO_NEW>::IterateTyped( 3679 chunk, 3680 [isolate, heap](SlotType type, Address host_addr, Address slot) { 3681 return UpdateTypedSlotHelper::UpdateTypedSlot( 3682 isolate, type, slot, [heap](Object** slot) { 3683 return CheckAndUpdateOldToNewSlot( 3684 heap, reinterpret_cast<Address>(slot)); 3685 }); 3686 }); 3687 } 3688 } 3689 3690 static SlotCallbackResult CheckAndUpdateOldToNewSlot(Heap* heap, 3691 Address slot_address) { 3692 // There may be concurrent action on slots in dead objects. Concurrent 3693 // sweeper threads may overwrite the slot content with a free space object. 3694 // Moreover, the pointed-to object may also get concurrently overwritten 3695 // with a free space object. The sweeper always gets priority performing 3696 // these writes. 3697 base::NoBarrierAtomicValue<Object*>* slot = 3698 base::NoBarrierAtomicValue<Object*>::FromAddress(slot_address); 3699 Object* slot_reference = slot->Value(); 3700 if (heap->InFromSpace(slot_reference)) { 3701 HeapObject* heap_object = reinterpret_cast<HeapObject*>(slot_reference); 3702 DCHECK(heap_object->IsHeapObject()); 3703 MapWord map_word = heap_object->map_word(); 3704 // There could still be stale pointers in large object space, map space, 3705 // and old space for pages that have been promoted. 3706 if (map_word.IsForwardingAddress()) { 3707 // A sweeper thread may concurrently write a size value which looks like 3708 // a forwarding pointer. We have to ignore these values. 3709 if (map_word.ToRawValue() < Page::kPageSize) { 3710 return REMOVE_SLOT; 3711 } 3712 // Update the corresponding slot only if the slot content did not 3713 // change in the meantime. This may happen when a concurrent sweeper 3714 // thread stored a free space object at that memory location. 3715 slot->TrySetValue(slot_reference, map_word.ToForwardingAddress()); 3716 } 3717 // If the object was in from space before and is after executing the 3718 // callback in to space, the object is still live. 3719 // Unfortunately, we do not know about the slot. It could be in a 3720 // just freed free space object. 3721 if (heap->InToSpace(slot->Value())) { 3722 return KEEP_SLOT; 3723 } 3724 } else if (heap->InToSpace(slot_reference)) { 3725 // Slots can point to "to" space if the page has been moved, or if the 3726 // slot has been recorded multiple times in the remembered set. Since 3727 // there is no forwarding information present we need to check the 3728 // markbits to determine liveness. 3729 if (ObjectMarking::IsBlack(reinterpret_cast<HeapObject*>(slot_reference))) 3730 return KEEP_SLOT; 3731 } else { 3732 DCHECK(!heap->InNewSpace(slot_reference)); 3733 } 3734 return REMOVE_SLOT; 3735 } 3736 }; 3737 3738 int NumberOfPointerUpdateTasks(int pages) { 3739 if (!FLAG_parallel_pointer_update) return 1; 3740 const int available_cores = Max( 3741 1, static_cast<int>( 3742 V8::GetCurrentPlatform()->NumberOfAvailableBackgroundThreads())); 3743 const int kPagesPerTask = 4; 3744 return Min(available_cores, (pages + kPagesPerTask - 1) / kPagesPerTask); 3745 } 3746 3747 template <PointerDirection direction> 3748 void UpdatePointersInParallel(Heap* heap, base::Semaphore* semaphore) { 3749 PageParallelJob<PointerUpdateJobTraits<direction> > job( 3750 heap, heap->isolate()->cancelable_task_manager(), semaphore); 3751 RememberedSet<direction>::IterateMemoryChunks( 3752 heap, [&job](MemoryChunk* chunk) { job.AddPage(chunk, 0); }); 3753 int num_pages = job.NumberOfPages(); 3754 int num_tasks = NumberOfPointerUpdateTasks(num_pages); 3755 job.Run(num_tasks, [](int i) { return 0; }); 3756 } 3757 3758 class ToSpacePointerUpdateJobTraits { 3759 public: 3760 typedef std::pair<Address, Address> PerPageData; 3761 typedef PointersUpdatingVisitor* PerTaskData; 3762 3763 static bool ProcessPageInParallel(Heap* heap, PerTaskData visitor, 3764 MemoryChunk* chunk, PerPageData limits) { 3765 if (chunk->IsFlagSet(Page::PAGE_NEW_NEW_PROMOTION)) { 3766 // New->new promoted pages contain garbage so they require iteration 3767 // using markbits. 3768 ProcessPageInParallelVisitLive(heap, visitor, chunk, limits); 3769 } else { 3770 ProcessPageInParallelVisitAll(heap, visitor, chunk, limits); 3771 } 3772 return true; 3773 } 3774 3775 static const bool NeedSequentialFinalization = false; 3776 static void FinalizePageSequentially(Heap*, MemoryChunk*, bool, PerPageData) { 3777 } 3778 3779 private: 3780 static void ProcessPageInParallelVisitAll(Heap* heap, PerTaskData visitor, 3781 MemoryChunk* chunk, 3782 PerPageData limits) { 3783 for (Address cur = limits.first; cur < limits.second;) { 3784 HeapObject* object = HeapObject::FromAddress(cur); 3785 Map* map = object->map(); 3786 int size = object->SizeFromMap(map); 3787 object->IterateBody(map->instance_type(), size, visitor); 3788 cur += size; 3789 } 3790 } 3791 3792 static void ProcessPageInParallelVisitLive(Heap* heap, PerTaskData visitor, 3793 MemoryChunk* chunk, 3794 PerPageData limits) { 3795 LiveObjectIterator<kBlackObjects> it(chunk); 3796 HeapObject* object = NULL; 3797 while ((object = it.Next()) != NULL) { 3798 Map* map = object->map(); 3799 int size = object->SizeFromMap(map); 3800 object->IterateBody(map->instance_type(), size, visitor); 3801 } 3802 } 3803 }; 3804 3805 void UpdateToSpacePointersInParallel(Heap* heap, base::Semaphore* semaphore) { 3806 PageParallelJob<ToSpacePointerUpdateJobTraits> job( 3807 heap, heap->isolate()->cancelable_task_manager(), semaphore); 3808 Address space_start = heap->new_space()->bottom(); 3809 Address space_end = heap->new_space()->top(); 3810 for (Page* page : PageRange(space_start, space_end)) { 3811 Address start = 3812 page->Contains(space_start) ? space_start : page->area_start(); 3813 Address end = page->Contains(space_end) ? space_end : page->area_end(); 3814 job.AddPage(page, std::make_pair(start, end)); 3815 } 3816 PointersUpdatingVisitor visitor; 3817 int num_tasks = FLAG_parallel_pointer_update ? job.NumberOfPages() : 1; 3818 job.Run(num_tasks, [&visitor](int i) { return &visitor; }); 3819 } 3820 3821 void MarkCompactCollector::UpdatePointersAfterEvacuation() { 3822 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_EVACUATE_UPDATE_POINTERS); 3823 3824 PointersUpdatingVisitor updating_visitor; 3825 3826 { 3827 TRACE_GC(heap()->tracer(), 3828 GCTracer::Scope::MC_EVACUATE_UPDATE_POINTERS_TO_NEW); 3829 UpdateToSpacePointersInParallel(heap_, &page_parallel_job_semaphore_); 3830 // Update roots. 3831 heap_->IterateRoots(&updating_visitor, VISIT_ALL_IN_SWEEP_NEWSPACE); 3832 UpdatePointersInParallel<OLD_TO_NEW>(heap_, &page_parallel_job_semaphore_); 3833 } 3834 3835 { 3836 Heap* heap = this->heap(); 3837 TRACE_GC(heap->tracer(), 3838 GCTracer::Scope::MC_EVACUATE_UPDATE_POINTERS_TO_EVACUATED); 3839 UpdatePointersInParallel<OLD_TO_OLD>(heap_, &page_parallel_job_semaphore_); 3840 } 3841 3842 { 3843 TRACE_GC(heap()->tracer(), 3844 GCTracer::Scope::MC_EVACUATE_UPDATE_POINTERS_WEAK); 3845 // Update pointers from external string table. 3846 heap_->UpdateReferencesInExternalStringTable( 3847 &UpdateReferenceInExternalStringTableEntry); 3848 3849 EvacuationWeakObjectRetainer evacuation_object_retainer; 3850 heap()->ProcessWeakListRoots(&evacuation_object_retainer); 3851 } 3852 } 3853 3854 3855 void MarkCompactCollector::ReleaseEvacuationCandidates() { 3856 for (Page* p : old_space_evacuation_pages_) { 3857 if (!p->IsEvacuationCandidate()) continue; 3858 PagedSpace* space = static_cast<PagedSpace*>(p->owner()); 3859 p->ResetLiveBytes(); 3860 CHECK(p->SweepingDone()); 3861 space->ReleasePage(p); 3862 } 3863 old_space_evacuation_pages_.Rewind(0); 3864 compacting_ = false; 3865 heap()->memory_allocator()->unmapper()->FreeQueuedChunks(); 3866 } 3867 3868 int MarkCompactCollector::Sweeper::ParallelSweepSpace(AllocationSpace identity, 3869 int required_freed_bytes, 3870 int max_pages) { 3871 int max_freed = 0; 3872 int pages_freed = 0; 3873 Page* page = nullptr; 3874 while ((page = GetSweepingPageSafe(identity)) != nullptr) { 3875 int freed = ParallelSweepPage(page, identity); 3876 pages_freed += 1; 3877 DCHECK_GE(freed, 0); 3878 max_freed = Max(max_freed, freed); 3879 if ((required_freed_bytes) > 0 && (max_freed >= required_freed_bytes)) 3880 return max_freed; 3881 if ((max_pages > 0) && (pages_freed >= max_pages)) return max_freed; 3882 } 3883 return max_freed; 3884 } 3885 3886 int MarkCompactCollector::Sweeper::ParallelSweepPage(Page* page, 3887 AllocationSpace identity) { 3888 int max_freed = 0; 3889 { 3890 base::LockGuard<base::Mutex> guard(page->mutex()); 3891 // If this page was already swept in the meantime, we can return here. 3892 if (page->SweepingDone()) return 0; 3893 DCHECK_EQ(Page::kSweepingPending, 3894 page->concurrent_sweeping_state().Value()); 3895 page->concurrent_sweeping_state().SetValue(Page::kSweepingInProgress); 3896 const Sweeper::FreeSpaceTreatmentMode free_space_mode = 3897 Heap::ShouldZapGarbage() ? ZAP_FREE_SPACE : IGNORE_FREE_SPACE; 3898 if (identity == NEW_SPACE) { 3899 RawSweep(page, IGNORE_FREE_LIST, free_space_mode); 3900 } else { 3901 max_freed = RawSweep(page, REBUILD_FREE_LIST, free_space_mode); 3902 } 3903 DCHECK(page->SweepingDone()); 3904 3905 // After finishing sweeping of a page we clean up its remembered set. 3906 if (page->typed_old_to_new_slots()) { 3907 page->typed_old_to_new_slots()->FreeToBeFreedChunks(); 3908 } 3909 if (page->old_to_new_slots()) { 3910 page->old_to_new_slots()->FreeToBeFreedBuckets(); 3911 } 3912 } 3913 3914 { 3915 base::LockGuard<base::Mutex> guard(&mutex_); 3916 swept_list_[identity].Add(page); 3917 } 3918 return max_freed; 3919 } 3920 3921 void MarkCompactCollector::Sweeper::AddPage(AllocationSpace space, Page* page) { 3922 DCHECK(!FLAG_concurrent_sweeping || !AreSweeperTasksRunning()); 3923 PrepareToBeSweptPage(space, page); 3924 sweeping_list_[space].push_back(page); 3925 } 3926 3927 void MarkCompactCollector::Sweeper::PrepareToBeSweptPage(AllocationSpace space, 3928 Page* page) { 3929 page->concurrent_sweeping_state().SetValue(Page::kSweepingPending); 3930 DCHECK_GE(page->area_size(), static_cast<size_t>(page->LiveBytes())); 3931 size_t to_sweep = page->area_size() - page->LiveBytes(); 3932 if (space != NEW_SPACE) 3933 heap_->paged_space(space)->accounting_stats_.ShrinkSpace(to_sweep); 3934 } 3935 3936 Page* MarkCompactCollector::Sweeper::GetSweepingPageSafe( 3937 AllocationSpace space) { 3938 base::LockGuard<base::Mutex> guard(&mutex_); 3939 Page* page = nullptr; 3940 if (!sweeping_list_[space].empty()) { 3941 page = sweeping_list_[space].front(); 3942 sweeping_list_[space].pop_front(); 3943 } 3944 return page; 3945 } 3946 3947 void MarkCompactCollector::Sweeper::AddSweepingPageSafe(AllocationSpace space, 3948 Page* page) { 3949 base::LockGuard<base::Mutex> guard(&mutex_); 3950 sweeping_list_[space].push_back(page); 3951 } 3952 3953 void MarkCompactCollector::StartSweepSpace(PagedSpace* space) { 3954 space->ClearStats(); 3955 3956 int will_be_swept = 0; 3957 bool unused_page_present = false; 3958 3959 // Loop needs to support deletion if live bytes == 0 for a page. 3960 for (auto it = space->begin(); it != space->end();) { 3961 Page* p = *(it++); 3962 DCHECK(p->SweepingDone()); 3963 3964 if (p->IsEvacuationCandidate()) { 3965 // Will be processed in EvacuateNewSpaceAndCandidates. 3966 DCHECK(evacuation_candidates_.length() > 0); 3967 continue; 3968 } 3969 3970 if (p->IsFlagSet(Page::NEVER_ALLOCATE_ON_PAGE)) { 3971 // We need to sweep the page to get it into an iterable state again. Note 3972 // that this adds unusable memory into the free list that is later on 3973 // (in the free list) dropped again. Since we only use the flag for 3974 // testing this is fine. 3975 p->concurrent_sweeping_state().SetValue(Page::kSweepingInProgress); 3976 Sweeper::RawSweep(p, Sweeper::IGNORE_FREE_LIST, 3977 Heap::ShouldZapGarbage() ? Sweeper::ZAP_FREE_SPACE 3978 : Sweeper::IGNORE_FREE_SPACE); 3979 continue; 3980 } 3981 3982 // One unused page is kept, all further are released before sweeping them. 3983 if (p->LiveBytes() == 0) { 3984 if (unused_page_present) { 3985 if (FLAG_gc_verbose) { 3986 PrintIsolate(isolate(), "sweeping: released page: %p", 3987 static_cast<void*>(p)); 3988 } 3989 ArrayBufferTracker::FreeAll(p); 3990 space->ReleasePage(p); 3991 continue; 3992 } 3993 unused_page_present = true; 3994 } 3995 3996 sweeper().AddPage(space->identity(), p); 3997 will_be_swept++; 3998 } 3999 4000 if (FLAG_gc_verbose) { 4001 PrintIsolate(isolate(), "sweeping: space=%s initialized_for_sweeping=%d", 4002 AllocationSpaceName(space->identity()), will_be_swept); 4003 } 4004 } 4005 4006 void MarkCompactCollector::StartSweepSpaces() { 4007 TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_SWEEP); 4008 #ifdef DEBUG 4009 state_ = SWEEP_SPACES; 4010 #endif 4011 4012 { 4013 { 4014 GCTracer::Scope sweep_scope(heap()->tracer(), 4015 GCTracer::Scope::MC_SWEEP_OLD); 4016 StartSweepSpace(heap()->old_space()); 4017 } 4018 { 4019 GCTracer::Scope sweep_scope(heap()->tracer(), 4020 GCTracer::Scope::MC_SWEEP_CODE); 4021 StartSweepSpace(heap()->code_space()); 4022 } 4023 { 4024 GCTracer::Scope sweep_scope(heap()->tracer(), 4025 GCTracer::Scope::MC_SWEEP_MAP); 4026 StartSweepSpace(heap()->map_space()); 4027 } 4028 sweeper().StartSweeping(); 4029 } 4030 4031 // Deallocate unmarked large objects. 4032 heap_->lo_space()->FreeUnmarkedObjects(); 4033 } 4034 4035 Isolate* MarkCompactCollector::isolate() const { return heap_->isolate(); } 4036 4037 4038 void MarkCompactCollector::Initialize() { 4039 MarkCompactMarkingVisitor::Initialize(); 4040 IncrementalMarking::Initialize(); 4041 } 4042 4043 void MarkCompactCollector::RecordCodeEntrySlot(HeapObject* host, Address slot, 4044 Code* target) { 4045 Page* target_page = Page::FromAddress(reinterpret_cast<Address>(target)); 4046 Page* source_page = Page::FromAddress(reinterpret_cast<Address>(host)); 4047 if (target_page->IsEvacuationCandidate() && 4048 !ShouldSkipEvacuationSlotRecording(host)) { 4049 // TODO(ulan): remove this check after investigating crbug.com/414964. 4050 CHECK(target->IsCode()); 4051 RememberedSet<OLD_TO_OLD>::InsertTyped( 4052 source_page, reinterpret_cast<Address>(host), CODE_ENTRY_SLOT, slot); 4053 } 4054 } 4055 4056 4057 void MarkCompactCollector::RecordCodeTargetPatch(Address pc, Code* target) { 4058 DCHECK(heap()->gc_state() == Heap::MARK_COMPACT); 4059 if (is_compacting()) { 4060 Code* host = 4061 isolate()->inner_pointer_to_code_cache()->GcSafeFindCodeForInnerPointer( 4062 pc); 4063 if (ObjectMarking::IsBlack(host)) { 4064 RelocInfo rinfo(isolate(), pc, RelocInfo::CODE_TARGET, 0, host); 4065 // The target is always in old space, we don't have to record the slot in 4066 // the old-to-new remembered set. 4067 DCHECK(!heap()->InNewSpace(target)); 4068 RecordRelocSlot(host, &rinfo, target); 4069 } 4070 } 4071 } 4072 4073 } // namespace internal 4074 } // namespace v8 4075