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 #ifndef V8_HEAP_MARK_COMPACT_H_ 6 #define V8_HEAP_MARK_COMPACT_H_ 7 8 #include <vector> 9 10 #include "src/heap/concurrent-marking.h" 11 #include "src/heap/marking.h" 12 #include "src/heap/objects-visiting.h" 13 #include "src/heap/spaces.h" 14 #include "src/heap/sweeper.h" 15 #include "src/heap/worklist.h" 16 17 namespace v8 { 18 namespace internal { 19 20 // Forward declarations. 21 class EvacuationJobTraits; 22 class HeapObjectVisitor; 23 class ItemParallelJob; 24 class MigrationObserver; 25 class RecordMigratedSlotVisitor; 26 class UpdatingItem; 27 class YoungGenerationMarkingVisitor; 28 29 template <typename ConcreteState, AccessMode access_mode> 30 class MarkingStateBase { 31 public: MarkBitFrom(HeapObject * obj)32 V8_INLINE MarkBit MarkBitFrom(HeapObject* obj) { 33 return MarkBitFrom(MemoryChunk::FromAddress(obj->address()), 34 obj->address()); 35 } 36 MarkBitFrom(MemoryChunk * p,Address addr)37 V8_INLINE MarkBit MarkBitFrom(MemoryChunk* p, Address addr) { 38 return static_cast<ConcreteState*>(this)->bitmap(p)->MarkBitFromIndex( 39 p->AddressToMarkbitIndex(addr)); 40 } 41 Color(HeapObject * obj)42 Marking::ObjectColor Color(HeapObject* obj) { 43 return Marking::Color(MarkBitFrom(obj)); 44 } 45 IsImpossible(HeapObject * obj)46 V8_INLINE bool IsImpossible(HeapObject* obj) { 47 return Marking::IsImpossible<access_mode>(MarkBitFrom(obj)); 48 } 49 IsBlack(HeapObject * obj)50 V8_INLINE bool IsBlack(HeapObject* obj) { 51 return Marking::IsBlack<access_mode>(MarkBitFrom(obj)); 52 } 53 IsWhite(HeapObject * obj)54 V8_INLINE bool IsWhite(HeapObject* obj) { 55 return Marking::IsWhite<access_mode>(MarkBitFrom(obj)); 56 } 57 IsGrey(HeapObject * obj)58 V8_INLINE bool IsGrey(HeapObject* obj) { 59 return Marking::IsGrey<access_mode>(MarkBitFrom(obj)); 60 } 61 IsBlackOrGrey(HeapObject * obj)62 V8_INLINE bool IsBlackOrGrey(HeapObject* obj) { 63 return Marking::IsBlackOrGrey<access_mode>(MarkBitFrom(obj)); 64 } 65 WhiteToGrey(HeapObject * obj)66 V8_INLINE bool WhiteToGrey(HeapObject* obj) { 67 return Marking::WhiteToGrey<access_mode>(MarkBitFrom(obj)); 68 } 69 WhiteToBlack(HeapObject * obj)70 V8_INLINE bool WhiteToBlack(HeapObject* obj) { 71 return WhiteToGrey(obj) && GreyToBlack(obj); 72 } 73 GreyToBlack(HeapObject * obj)74 V8_INLINE bool GreyToBlack(HeapObject* obj) { 75 MemoryChunk* p = MemoryChunk::FromAddress(obj->address()); 76 MarkBit markbit = MarkBitFrom(p, obj->address()); 77 if (!Marking::GreyToBlack<access_mode>(markbit)) return false; 78 static_cast<ConcreteState*>(this)->IncrementLiveBytes(p, obj->Size()); 79 return true; 80 } 81 ClearLiveness(MemoryChunk * chunk)82 void ClearLiveness(MemoryChunk* chunk) { 83 static_cast<ConcreteState*>(this)->bitmap(chunk)->Clear(); 84 static_cast<ConcreteState*>(this)->SetLiveBytes(chunk, 0); 85 } 86 }; 87 88 class MarkBitCellIterator { 89 public: MarkBitCellIterator(MemoryChunk * chunk,Bitmap * bitmap)90 MarkBitCellIterator(MemoryChunk* chunk, Bitmap* bitmap) : chunk_(chunk) { 91 DCHECK(Bitmap::IsCellAligned( 92 chunk_->AddressToMarkbitIndex(chunk_->area_start()))); 93 DCHECK(Bitmap::IsCellAligned( 94 chunk_->AddressToMarkbitIndex(chunk_->area_end()))); 95 last_cell_index_ = 96 Bitmap::IndexToCell(chunk_->AddressToMarkbitIndex(chunk_->area_end())); 97 cell_base_ = chunk_->area_start(); 98 cell_index_ = 99 Bitmap::IndexToCell(chunk_->AddressToMarkbitIndex(cell_base_)); 100 cells_ = bitmap->cells(); 101 } 102 Done()103 inline bool Done() { return cell_index_ >= last_cell_index_; } 104 HasNext()105 inline bool HasNext() { return cell_index_ < last_cell_index_ - 1; } 106 CurrentCell()107 inline MarkBit::CellType* CurrentCell() { 108 DCHECK_EQ(cell_index_, Bitmap::IndexToCell(Bitmap::CellAlignIndex( 109 chunk_->AddressToMarkbitIndex(cell_base_)))); 110 return &cells_[cell_index_]; 111 } 112 CurrentCellBase()113 inline Address CurrentCellBase() { 114 DCHECK_EQ(cell_index_, Bitmap::IndexToCell(Bitmap::CellAlignIndex( 115 chunk_->AddressToMarkbitIndex(cell_base_)))); 116 return cell_base_; 117 } 118 Advance()119 V8_WARN_UNUSED_RESULT inline bool Advance() { 120 cell_base_ += Bitmap::kBitsPerCell * kPointerSize; 121 return ++cell_index_ != last_cell_index_; 122 } 123 Advance(unsigned int new_cell_index)124 inline bool Advance(unsigned int new_cell_index) { 125 if (new_cell_index != cell_index_) { 126 DCHECK_GT(new_cell_index, cell_index_); 127 DCHECK_LE(new_cell_index, last_cell_index_); 128 unsigned int diff = new_cell_index - cell_index_; 129 cell_index_ = new_cell_index; 130 cell_base_ += diff * (Bitmap::kBitsPerCell * kPointerSize); 131 return true; 132 } 133 return false; 134 } 135 136 // Return the next mark bit cell. If there is no next it returns 0; PeekNext()137 inline MarkBit::CellType PeekNext() { 138 if (HasNext()) { 139 return cells_[cell_index_ + 1]; 140 } 141 return 0; 142 } 143 144 private: 145 MemoryChunk* chunk_; 146 MarkBit::CellType* cells_; 147 unsigned int last_cell_index_; 148 unsigned int cell_index_; 149 Address cell_base_; 150 }; 151 152 enum LiveObjectIterationMode { 153 kBlackObjects, 154 kGreyObjects, 155 kAllLiveObjects 156 }; 157 158 template <LiveObjectIterationMode mode> 159 class LiveObjectRange { 160 public: 161 class iterator { 162 public: 163 using value_type = std::pair<HeapObject*, int /* size */>; 164 using pointer = const value_type*; 165 using reference = const value_type&; 166 using iterator_category = std::forward_iterator_tag; 167 168 inline iterator(MemoryChunk* chunk, Bitmap* bitmap, Address start); 169 170 inline iterator& operator++(); 171 inline iterator operator++(int); 172 173 bool operator==(iterator other) const { 174 return current_object_ == other.current_object_; 175 } 176 177 bool operator!=(iterator other) const { return !(*this == other); } 178 179 value_type operator*() { 180 return std::make_pair(current_object_, current_size_); 181 } 182 183 private: 184 inline void AdvanceToNextValidObject(); 185 186 MemoryChunk* const chunk_; 187 Map* const one_word_filler_map_; 188 Map* const two_word_filler_map_; 189 Map* const free_space_map_; 190 MarkBitCellIterator it_; 191 Address cell_base_; 192 MarkBit::CellType current_cell_; 193 HeapObject* current_object_; 194 int current_size_; 195 }; 196 LiveObjectRange(MemoryChunk * chunk,Bitmap * bitmap)197 LiveObjectRange(MemoryChunk* chunk, Bitmap* bitmap) 198 : chunk_(chunk), 199 bitmap_(bitmap), 200 start_(chunk_->area_start()), 201 end_(chunk->area_end()) {} 202 203 inline iterator begin(); 204 inline iterator end(); 205 206 private: 207 MemoryChunk* const chunk_; 208 Bitmap* bitmap_; 209 Address start_; 210 Address end_; 211 }; 212 213 class LiveObjectVisitor : AllStatic { 214 public: 215 enum IterationMode { 216 kKeepMarking, 217 kClearMarkbits, 218 }; 219 220 // Visits black objects on a MemoryChunk until the Visitor returns |false| for 221 // an object. If IterationMode::kClearMarkbits is passed the markbits and 222 // slots for visited objects are cleared for each successfully visited object. 223 template <class Visitor, typename MarkingState> 224 static bool VisitBlackObjects(MemoryChunk* chunk, MarkingState* state, 225 Visitor* visitor, IterationMode iteration_mode, 226 HeapObject** failed_object); 227 228 // Visits black objects on a MemoryChunk. The visitor is not allowed to fail 229 // visitation for an object. 230 template <class Visitor, typename MarkingState> 231 static void VisitBlackObjectsNoFail(MemoryChunk* chunk, MarkingState* state, 232 Visitor* visitor, 233 IterationMode iteration_mode); 234 235 // Visits black objects on a MemoryChunk. The visitor is not allowed to fail 236 // visitation for an object. 237 template <class Visitor, typename MarkingState> 238 static void VisitGreyObjectsNoFail(MemoryChunk* chunk, MarkingState* state, 239 Visitor* visitor, 240 IterationMode iteration_mode); 241 242 template <typename MarkingState> 243 static void RecomputeLiveBytes(MemoryChunk* chunk, MarkingState* state); 244 }; 245 246 enum PageEvacuationMode { NEW_TO_NEW, NEW_TO_OLD }; 247 enum MarkingTreatmentMode { KEEP, CLEAR }; 248 enum class RememberedSetUpdatingMode { ALL, OLD_TO_NEW_ONLY }; 249 250 // Base class for minor and full MC collectors. 251 class MarkCompactCollectorBase { 252 public: ~MarkCompactCollectorBase()253 virtual ~MarkCompactCollectorBase() {} 254 255 virtual void SetUp() = 0; 256 virtual void TearDown() = 0; 257 virtual void CollectGarbage() = 0; 258 heap()259 inline Heap* heap() const { return heap_; } 260 inline Isolate* isolate(); 261 262 protected: 263 static const int kMainThread = 0; MarkCompactCollectorBase(Heap * heap)264 explicit MarkCompactCollectorBase(Heap* heap) 265 : heap_(heap), old_to_new_slots_(0) {} 266 267 // Marking operations for objects reachable from roots. 268 virtual void MarkLiveObjects() = 0; 269 // Mark objects reachable (transitively) from objects in the marking 270 // work list. 271 virtual void ProcessMarkingWorklist() = 0; 272 // Clear non-live references held in side data structures. 273 virtual void ClearNonLiveReferences() = 0; 274 virtual void EvacuatePrologue() = 0; 275 virtual void EvacuateEpilogue() = 0; 276 virtual void Evacuate() = 0; 277 virtual void EvacuatePagesInParallel() = 0; 278 virtual void UpdatePointersAfterEvacuation() = 0; 279 virtual UpdatingItem* CreateToSpaceUpdatingItem(MemoryChunk* chunk, 280 Address start, 281 Address end) = 0; 282 virtual UpdatingItem* CreateRememberedSetUpdatingItem( 283 MemoryChunk* chunk, RememberedSetUpdatingMode updating_mode) = 0; 284 285 template <class Evacuator, class Collector> 286 void CreateAndExecuteEvacuationTasks( 287 Collector* collector, ItemParallelJob* job, 288 RecordMigratedSlotVisitor* record_visitor, 289 MigrationObserver* migration_observer, const intptr_t live_bytes); 290 291 // Returns whether this page should be moved according to heuristics. 292 bool ShouldMovePage(Page* p, intptr_t live_bytes); 293 294 int CollectToSpaceUpdatingItems(ItemParallelJob* job); 295 template <typename IterateableSpace> 296 int CollectRememberedSetUpdatingItems(ItemParallelJob* job, 297 IterateableSpace* space, 298 RememberedSetUpdatingMode mode); 299 300 int NumberOfParallelCompactionTasks(int pages); 301 int NumberOfParallelPointerUpdateTasks(int pages, int slots); 302 int NumberOfParallelToSpacePointerUpdateTasks(int pages); 303 304 Heap* heap_; 305 // Number of old to new slots. Should be computed during MarkLiveObjects. 306 // -1 indicates that the value couldn't be computed. 307 int old_to_new_slots_; 308 }; 309 310 class MinorMarkingState final 311 : public MarkingStateBase<MinorMarkingState, AccessMode::ATOMIC> { 312 public: bitmap(const MemoryChunk * chunk)313 Bitmap* bitmap(const MemoryChunk* chunk) const { 314 return chunk->young_generation_bitmap_; 315 } 316 IncrementLiveBytes(MemoryChunk * chunk,intptr_t by)317 void IncrementLiveBytes(MemoryChunk* chunk, intptr_t by) { 318 chunk->young_generation_live_byte_count_ += by; 319 } 320 live_bytes(MemoryChunk * chunk)321 intptr_t live_bytes(MemoryChunk* chunk) const { 322 return chunk->young_generation_live_byte_count_; 323 } 324 SetLiveBytes(MemoryChunk * chunk,intptr_t value)325 void SetLiveBytes(MemoryChunk* chunk, intptr_t value) { 326 chunk->young_generation_live_byte_count_ = value; 327 } 328 }; 329 330 class MinorNonAtomicMarkingState final 331 : public MarkingStateBase<MinorNonAtomicMarkingState, 332 AccessMode::NON_ATOMIC> { 333 public: bitmap(const MemoryChunk * chunk)334 Bitmap* bitmap(const MemoryChunk* chunk) const { 335 return chunk->young_generation_bitmap_; 336 } 337 IncrementLiveBytes(MemoryChunk * chunk,intptr_t by)338 void IncrementLiveBytes(MemoryChunk* chunk, intptr_t by) { 339 chunk->young_generation_live_byte_count_ += by; 340 } 341 live_bytes(MemoryChunk * chunk)342 intptr_t live_bytes(MemoryChunk* chunk) const { 343 return chunk->young_generation_live_byte_count_; 344 } 345 SetLiveBytes(MemoryChunk * chunk,intptr_t value)346 void SetLiveBytes(MemoryChunk* chunk, intptr_t value) { 347 chunk->young_generation_live_byte_count_ = value; 348 } 349 }; 350 351 // This marking state is used when concurrent marking is running. 352 class IncrementalMarkingState final 353 : public MarkingStateBase<IncrementalMarkingState, AccessMode::ATOMIC> { 354 public: bitmap(const MemoryChunk * chunk)355 Bitmap* bitmap(const MemoryChunk* chunk) const { 356 return Bitmap::FromAddress(chunk->address() + MemoryChunk::kHeaderSize); 357 } 358 359 // Concurrent marking uses local live bytes. IncrementLiveBytes(MemoryChunk * chunk,intptr_t by)360 void IncrementLiveBytes(MemoryChunk* chunk, intptr_t by) { 361 chunk->live_byte_count_ += by; 362 } 363 live_bytes(MemoryChunk * chunk)364 intptr_t live_bytes(MemoryChunk* chunk) const { 365 return chunk->live_byte_count_; 366 } 367 SetLiveBytes(MemoryChunk * chunk,intptr_t value)368 void SetLiveBytes(MemoryChunk* chunk, intptr_t value) { 369 chunk->live_byte_count_ = value; 370 } 371 }; 372 373 class MajorAtomicMarkingState final 374 : public MarkingStateBase<MajorAtomicMarkingState, AccessMode::ATOMIC> { 375 public: bitmap(const MemoryChunk * chunk)376 Bitmap* bitmap(const MemoryChunk* chunk) const { 377 return Bitmap::FromAddress(chunk->address() + MemoryChunk::kHeaderSize); 378 } 379 IncrementLiveBytes(MemoryChunk * chunk,intptr_t by)380 void IncrementLiveBytes(MemoryChunk* chunk, intptr_t by) { 381 chunk->live_byte_count_ += by; 382 } 383 live_bytes(MemoryChunk * chunk)384 intptr_t live_bytes(MemoryChunk* chunk) const { 385 return chunk->live_byte_count_; 386 } 387 SetLiveBytes(MemoryChunk * chunk,intptr_t value)388 void SetLiveBytes(MemoryChunk* chunk, intptr_t value) { 389 chunk->live_byte_count_ = value; 390 } 391 }; 392 393 class MajorNonAtomicMarkingState final 394 : public MarkingStateBase<MajorNonAtomicMarkingState, 395 AccessMode::NON_ATOMIC> { 396 public: bitmap(const MemoryChunk * chunk)397 Bitmap* bitmap(const MemoryChunk* chunk) const { 398 return Bitmap::FromAddress(chunk->address() + MemoryChunk::kHeaderSize); 399 } 400 IncrementLiveBytes(MemoryChunk * chunk,intptr_t by)401 void IncrementLiveBytes(MemoryChunk* chunk, intptr_t by) { 402 chunk->live_byte_count_ += by; 403 } 404 live_bytes(MemoryChunk * chunk)405 intptr_t live_bytes(MemoryChunk* chunk) const { 406 return chunk->live_byte_count_; 407 } 408 SetLiveBytes(MemoryChunk * chunk,intptr_t value)409 void SetLiveBytes(MemoryChunk* chunk, intptr_t value) { 410 chunk->live_byte_count_ = value; 411 } 412 }; 413 414 struct Ephemeron { 415 HeapObject* key; 416 HeapObject* value; 417 }; 418 419 typedef Worklist<Ephemeron, 64> EphemeronWorklist; 420 421 // Weak objects encountered during marking. 422 struct WeakObjects { 423 Worklist<TransitionArray*, 64> transition_arrays; 424 425 // Keep track of all EphemeronHashTables in the heap to process 426 // them in the atomic pause. 427 Worklist<EphemeronHashTable*, 64> ephemeron_hash_tables; 428 429 // Keep track of all ephemerons for concurrent marking tasks. Only store 430 // ephemerons in these Worklists if both key and value are unreachable at the 431 // moment. 432 // 433 // MarkCompactCollector::ProcessEphemeronsUntilFixpoint drains and fills these 434 // worklists. 435 // 436 // current_ephemerons is used as draining worklist in the current fixpoint 437 // iteration. 438 EphemeronWorklist current_ephemerons; 439 440 // Stores ephemerons to visit in the next fixpoint iteration. 441 EphemeronWorklist next_ephemerons; 442 443 // When draining the marking worklist new discovered ephemerons are pushed 444 // into this worklist. 445 EphemeronWorklist discovered_ephemerons; 446 447 // TODO(marja): For old space, we only need the slot, not the host 448 // object. Optimize this by adding a different storage for old space. 449 Worklist<std::pair<HeapObject*, HeapObjectReference**>, 64> weak_references; 450 Worklist<std::pair<HeapObject*, Code*>, 64> weak_objects_in_code; 451 }; 452 453 struct EphemeronMarking { 454 std::vector<HeapObject*> newly_discovered; 455 bool newly_discovered_overflowed; 456 size_t newly_discovered_limit; 457 }; 458 459 // Collector for young and old generation. 460 class MarkCompactCollector final : public MarkCompactCollectorBase { 461 public: 462 #ifdef V8_CONCURRENT_MARKING 463 using MarkingState = IncrementalMarkingState; 464 #else 465 using MarkingState = MajorNonAtomicMarkingState; 466 #endif // V8_CONCURRENT_MARKING 467 using NonAtomicMarkingState = MajorNonAtomicMarkingState; 468 // Wrapper for the shared and bailout worklists. 469 class MarkingWorklist { 470 public: 471 using ConcurrentMarkingWorklist = Worklist<HeapObject*, 64>; 472 473 // The heap parameter is not used but needed to match the sequential case. MarkingWorklist(Heap * heap)474 explicit MarkingWorklist(Heap* heap) {} 475 Push(HeapObject * object)476 void Push(HeapObject* object) { 477 bool success = shared_.Push(kMainThread, object); 478 USE(success); 479 DCHECK(success); 480 } 481 PushBailout(HeapObject * object)482 void PushBailout(HeapObject* object) { 483 bool success = bailout_.Push(kMainThread, object); 484 USE(success); 485 DCHECK(success); 486 } 487 Pop()488 HeapObject* Pop() { 489 HeapObject* result; 490 #ifdef V8_CONCURRENT_MARKING 491 if (bailout_.Pop(kMainThread, &result)) return result; 492 #endif 493 if (shared_.Pop(kMainThread, &result)) return result; 494 #ifdef V8_CONCURRENT_MARKING 495 // The expectation is that this work list is empty almost all the time 496 // and we can thus avoid the emptiness checks by putting it last. 497 if (on_hold_.Pop(kMainThread, &result)) return result; 498 #endif 499 return nullptr; 500 } 501 PopBailout()502 HeapObject* PopBailout() { 503 HeapObject* result; 504 #ifdef V8_CONCURRENT_MARKING 505 if (bailout_.Pop(kMainThread, &result)) return result; 506 #endif 507 return nullptr; 508 } 509 Clear()510 void Clear() { 511 bailout_.Clear(); 512 shared_.Clear(); 513 on_hold_.Clear(); 514 } 515 IsBailoutEmpty()516 bool IsBailoutEmpty() { return bailout_.IsLocalEmpty(kMainThread); } 517 IsEmpty()518 bool IsEmpty() { 519 return bailout_.IsLocalEmpty(kMainThread) && 520 shared_.IsLocalEmpty(kMainThread) && 521 on_hold_.IsLocalEmpty(kMainThread) && 522 bailout_.IsGlobalPoolEmpty() && shared_.IsGlobalPoolEmpty() && 523 on_hold_.IsGlobalPoolEmpty(); 524 } 525 Size()526 int Size() { 527 return static_cast<int>(bailout_.LocalSize(kMainThread) + 528 shared_.LocalSize(kMainThread) + 529 on_hold_.LocalSize(kMainThread)); 530 } 531 532 // Calls the specified callback on each element of the deques and replaces 533 // the element with the result of the callback. If the callback returns 534 // nullptr then the element is removed from the deque. 535 // The callback must accept HeapObject* and return HeapObject*. 536 template <typename Callback> Update(Callback callback)537 void Update(Callback callback) { 538 bailout_.Update(callback); 539 shared_.Update(callback); 540 on_hold_.Update(callback); 541 } 542 shared()543 ConcurrentMarkingWorklist* shared() { return &shared_; } bailout()544 ConcurrentMarkingWorklist* bailout() { return &bailout_; } on_hold()545 ConcurrentMarkingWorklist* on_hold() { return &on_hold_; } 546 Print()547 void Print() { 548 PrintWorklist("shared", &shared_); 549 PrintWorklist("bailout", &bailout_); 550 PrintWorklist("on_hold", &on_hold_); 551 } 552 553 private: 554 // Prints the stats about the global pool of the worklist. 555 void PrintWorklist(const char* worklist_name, 556 ConcurrentMarkingWorklist* worklist); 557 558 // Worklist used for most objects. 559 ConcurrentMarkingWorklist shared_; 560 561 // Concurrent marking uses this worklist to bail out of concurrently 562 // marking certain object types. These objects are handled later in a STW 563 // pause after concurrent marking has finished. 564 ConcurrentMarkingWorklist bailout_; 565 566 // Concurrent marking uses this worklist to bail out of marking objects 567 // in new space's linear allocation area. Used to avoid black allocation 568 // for new space. This allow the compiler to remove write barriers 569 // for freshly allocatd objects. 570 ConcurrentMarkingWorklist on_hold_; 571 }; 572 573 class RootMarkingVisitor; 574 class CustomRootBodyMarkingVisitor; 575 576 enum IterationMode { 577 kKeepMarking, 578 kClearMarkbits, 579 }; 580 marking_state()581 MarkingState* marking_state() { return &marking_state_; } 582 non_atomic_marking_state()583 NonAtomicMarkingState* non_atomic_marking_state() { 584 return &non_atomic_marking_state_; 585 } 586 587 void SetUp() override; 588 void TearDown() override; 589 // Performs a global garbage collection. 590 void CollectGarbage() override; 591 592 void CollectEvacuationCandidates(PagedSpace* space); 593 594 void AddEvacuationCandidate(Page* p); 595 596 // Prepares for GC by resetting relocation info in old and map spaces and 597 // choosing spaces to compact. 598 void Prepare(); 599 600 // Stop concurrent marking (either by preempting it right away or waiting for 601 // it to complete as requested by |stop_request|). 602 void FinishConcurrentMarking(ConcurrentMarking::StopRequest stop_request); 603 604 bool StartCompaction(); 605 606 void AbortCompaction(); 607 IsOnEvacuationCandidate(Object * obj)608 static inline bool IsOnEvacuationCandidate(Object* obj) { 609 return Page::FromAddress(reinterpret_cast<Address>(obj)) 610 ->IsEvacuationCandidate(); 611 } 612 IsOnEvacuationCandidate(MaybeObject * obj)613 static inline bool IsOnEvacuationCandidate(MaybeObject* obj) { 614 return Page::FromAddress(reinterpret_cast<Address>(obj)) 615 ->IsEvacuationCandidate(); 616 } 617 618 void RecordRelocSlot(Code* host, RelocInfo* rinfo, Object* target); 619 V8_INLINE static void RecordSlot(HeapObject* object, Object** slot, 620 HeapObject* target); 621 V8_INLINE static void RecordSlot(HeapObject* object, 622 HeapObjectReference** slot, 623 HeapObject* target); 624 void RecordLiveSlotsOnPage(Page* page); 625 626 void UpdateSlots(SlotsBuffer* buffer); 627 void UpdateSlotsRecordedIn(SlotsBuffer* buffer); 628 629 void ClearMarkbits(); 630 is_compacting()631 bool is_compacting() const { return compacting_; } 632 633 // Ensures that sweeping is finished. 634 // 635 // Note: Can only be called safely from main thread. 636 void EnsureSweepingCompleted(); 637 638 // Checks if sweeping is in progress right now on any space. sweeping_in_progress()639 bool sweeping_in_progress() const { return sweeper_->sweeping_in_progress(); } 640 set_evacuation(bool evacuation)641 void set_evacuation(bool evacuation) { evacuation_ = evacuation; } 642 evacuation()643 bool evacuation() const { return evacuation_; } 644 marking_worklist()645 MarkingWorklist* marking_worklist() { return &marking_worklist_; } 646 weak_objects()647 WeakObjects* weak_objects() { return &weak_objects_; } 648 AddTransitionArray(TransitionArray * array)649 void AddTransitionArray(TransitionArray* array) { 650 weak_objects_.transition_arrays.Push(kMainThread, array); 651 } 652 AddEphemeronHashTable(EphemeronHashTable * table)653 void AddEphemeronHashTable(EphemeronHashTable* table) { 654 weak_objects_.ephemeron_hash_tables.Push(kMainThread, table); 655 } 656 AddEphemeron(HeapObject * key,HeapObject * value)657 void AddEphemeron(HeapObject* key, HeapObject* value) { 658 weak_objects_.discovered_ephemerons.Push(kMainThread, 659 Ephemeron{key, value}); 660 } 661 AddWeakReference(HeapObject * host,HeapObjectReference ** slot)662 void AddWeakReference(HeapObject* host, HeapObjectReference** slot) { 663 weak_objects_.weak_references.Push(kMainThread, std::make_pair(host, slot)); 664 } 665 AddWeakObjectInCode(HeapObject * object,Code * code)666 void AddWeakObjectInCode(HeapObject* object, Code* code) { 667 weak_objects_.weak_objects_in_code.Push(kMainThread, 668 std::make_pair(object, code)); 669 } 670 AddNewlyDiscovered(HeapObject * object)671 void AddNewlyDiscovered(HeapObject* object) { 672 if (ephemeron_marking_.newly_discovered_overflowed) return; 673 674 if (ephemeron_marking_.newly_discovered.size() < 675 ephemeron_marking_.newly_discovered_limit) { 676 ephemeron_marking_.newly_discovered.push_back(object); 677 } else { 678 ephemeron_marking_.newly_discovered_overflowed = true; 679 } 680 } 681 ResetNewlyDiscovered()682 void ResetNewlyDiscovered() { 683 ephemeron_marking_.newly_discovered_overflowed = false; 684 ephemeron_marking_.newly_discovered.clear(); 685 } 686 sweeper()687 Sweeper* sweeper() { return sweeper_; } 688 689 #ifdef DEBUG 690 // Checks whether performing mark-compact collection. in_use()691 bool in_use() { return state_ > PREPARE_GC; } are_map_pointers_encoded()692 bool are_map_pointers_encoded() { return state_ == UPDATE_POINTERS; } 693 #endif 694 695 void VerifyMarking(); 696 #ifdef VERIFY_HEAP 697 void VerifyValidStoreAndSlotsBufferEntries(); 698 void VerifyMarkbitsAreClean(); 699 void VerifyMarkbitsAreDirty(PagedSpace* space); 700 void VerifyMarkbitsAreClean(PagedSpace* space); 701 void VerifyMarkbitsAreClean(NewSpace* space); 702 #endif 703 704 private: 705 explicit MarkCompactCollector(Heap* heap); 706 ~MarkCompactCollector(); 707 708 bool WillBeDeoptimized(Code* code); 709 710 void ComputeEvacuationHeuristics(size_t area_size, 711 int* target_fragmentation_percent, 712 size_t* max_evacuated_bytes); 713 714 void RecordObjectStats(); 715 716 // Finishes GC, performs heap verification if enabled. 717 void Finish(); 718 719 void MarkLiveObjects() override; 720 721 // Marks the object black and adds it to the marking work list. 722 // This is for non-incremental marking only. 723 V8_INLINE void MarkObject(HeapObject* host, HeapObject* obj); 724 725 // Marks the object black and adds it to the marking work list. 726 // This is for non-incremental marking only. 727 V8_INLINE void MarkRootObject(Root root, HeapObject* obj); 728 729 // Used by wrapper tracing. 730 V8_INLINE void MarkExternallyReferencedObject(HeapObject* obj); 731 732 // Mark the heap roots and all objects reachable from them. 733 void MarkRoots(RootVisitor* root_visitor, 734 ObjectVisitor* custom_root_body_visitor); 735 736 // Mark the string table specially. References to internalized strings from 737 // the string table are weak. 738 void MarkStringTable(ObjectVisitor* visitor); 739 740 // Marks object reachable from harmony weak maps and wrapper tracing. 741 void ProcessEphemeronMarking(); 742 743 // If the call-site of the top optimized code was not prepared for 744 // deoptimization, then treat embedded pointers in the code as strong as 745 // otherwise they can die and try to deoptimize the underlying code. 746 void ProcessTopOptimizedFrame(ObjectVisitor* visitor); 747 748 // Collects a list of dependent code from maps embedded in optimize code. 749 DependentCode* DependentCodeListFromNonLiveMaps(); 750 751 // Drains the main thread marking work list. Will mark all pending objects 752 // if no concurrent threads are running. 753 void ProcessMarkingWorklist() override; 754 755 enum class MarkingWorklistProcessingMode { 756 kDefault, 757 kTrackNewlyDiscoveredObjects 758 }; 759 760 template <MarkingWorklistProcessingMode mode> 761 void ProcessMarkingWorklistInternal(); 762 763 // Implements ephemeron semantics: Marks value if key is already reachable. 764 // Returns true if value was actually marked. 765 bool VisitEphemeron(HeapObject* key, HeapObject* value); 766 767 // Marks ephemerons and drains marking worklist iteratively 768 // until a fixpoint is reached. 769 void ProcessEphemeronsUntilFixpoint(); 770 771 // Drains ephemeron and marking worklists. Single iteration of the 772 // fixpoint iteration. 773 bool ProcessEphemerons(); 774 775 // Mark ephemerons and drain marking worklist with a linear algorithm. 776 // Only used if fixpoint iteration doesn't finish within a few iterations. 777 void ProcessEphemeronsLinear(); 778 779 // Perform Wrapper Tracing if in use. 780 void PerformWrapperTracing(); 781 782 // Callback function for telling whether the object *p is an unmarked 783 // heap object. 784 static bool IsUnmarkedHeapObject(Heap* heap, Object** p); 785 786 // Clear non-live references in weak cells, transition and descriptor arrays, 787 // and deoptimize dependent code of non-live maps. 788 void ClearNonLiveReferences() override; 789 void MarkDependentCodeForDeoptimization(); 790 // Checks if the given weak cell is a simple transition from the parent map 791 // of the given dead target. If so it clears the transition and trims 792 // the descriptor array of the parent if needed. 793 void ClearPotentialSimpleMapTransition(Map* dead_target); 794 void ClearPotentialSimpleMapTransition(Map* map, Map* dead_target); 795 // Compact every array in the global list of transition arrays and 796 // trim the corresponding descriptor array if a transition target is non-live. 797 void ClearFullMapTransitions(); 798 bool CompactTransitionArray(Map* map, TransitionArray* transitions, 799 DescriptorArray* descriptors); 800 void TrimDescriptorArray(Map* map, DescriptorArray* descriptors); 801 void TrimEnumCache(Map* map, DescriptorArray* descriptors); 802 803 // After all reachable objects have been marked those weak map entries 804 // with an unreachable key are removed from all encountered weak maps. 805 // The linked list of all encountered weak maps is destroyed. 806 void ClearWeakCollections(); 807 808 // Goes through the list of encountered weak references and clears those with 809 // dead values. If the value is a dead map and the parent map transitions to 810 // the dead map via weak cell, then this function also clears the map 811 // transition. 812 void ClearWeakReferences(); 813 void AbortWeakObjects(); 814 815 // Starts sweeping of spaces by contributing on the main thread and setting 816 // up other pages for sweeping. Does not start sweeper tasks. 817 void StartSweepSpaces(); 818 void StartSweepSpace(PagedSpace* space); 819 820 void EvacuatePrologue() override; 821 void EvacuateEpilogue() override; 822 void Evacuate() override; 823 void EvacuatePagesInParallel() override; 824 void UpdatePointersAfterEvacuation() override; 825 826 UpdatingItem* CreateToSpaceUpdatingItem(MemoryChunk* chunk, Address start, 827 Address end) override; 828 UpdatingItem* CreateRememberedSetUpdatingItem( 829 MemoryChunk* chunk, RememberedSetUpdatingMode updating_mode) override; 830 831 int CollectNewSpaceArrayBufferTrackerItems(ItemParallelJob* job); 832 int CollectOldSpaceArrayBufferTrackerItems(ItemParallelJob* job); 833 834 void ReleaseEvacuationCandidates(); 835 void PostProcessEvacuationCandidates(); 836 void ReportAbortedEvacuationCandidate(HeapObject* failed_object, Page* page); 837 838 void ClearMarkbitsInPagedSpace(PagedSpace* space); 839 void ClearMarkbitsInNewSpace(NewSpace* space); 840 841 static const int kEphemeronChunkSize = 8 * KB; 842 843 int NumberOfParallelEphemeronVisitingTasks(size_t elements); 844 845 base::Mutex mutex_; 846 base::Semaphore page_parallel_job_semaphore_; 847 848 #ifdef DEBUG 849 enum CollectorState { 850 IDLE, 851 PREPARE_GC, 852 MARK_LIVE_OBJECTS, 853 SWEEP_SPACES, 854 ENCODE_FORWARDING_ADDRESSES, 855 UPDATE_POINTERS, 856 RELOCATE_OBJECTS 857 }; 858 859 // The current stage of the collector. 860 CollectorState state_; 861 #endif 862 863 bool was_marked_incrementally_; 864 865 bool evacuation_; 866 867 // True if we are collecting slots to perform evacuation from evacuation 868 // candidates. 869 bool compacting_; 870 871 bool black_allocation_; 872 873 bool have_code_to_deoptimize_; 874 875 MarkingWorklist marking_worklist_; 876 WeakObjects weak_objects_; 877 EphemeronMarking ephemeron_marking_; 878 879 // Candidates for pages that should be evacuated. 880 std::vector<Page*> evacuation_candidates_; 881 // Pages that are actually processed during evacuation. 882 std::vector<Page*> old_space_evacuation_pages_; 883 std::vector<Page*> new_space_evacuation_pages_; 884 std::vector<std::pair<HeapObject*, Page*>> aborted_evacuation_candidates_; 885 886 Sweeper* sweeper_; 887 888 MarkingState marking_state_; 889 NonAtomicMarkingState non_atomic_marking_state_; 890 891 friend class EphemeronHashTableMarkingTask; 892 friend class FullEvacuator; 893 friend class Heap; 894 friend class RecordMigratedSlotVisitor; 895 }; 896 897 template <FixedArrayVisitationMode fixed_array_mode, 898 TraceRetainingPathMode retaining_path_mode, typename MarkingState> 899 class MarkingVisitor final 900 : public HeapVisitor< 901 int, 902 MarkingVisitor<fixed_array_mode, retaining_path_mode, MarkingState>> { 903 public: 904 typedef HeapVisitor< 905 int, MarkingVisitor<fixed_array_mode, retaining_path_mode, MarkingState>> 906 Parent; 907 908 V8_INLINE MarkingVisitor(MarkCompactCollector* collector, 909 MarkingState* marking_state); 910 ShouldVisitMapPointer()911 V8_INLINE bool ShouldVisitMapPointer() { return false; } 912 913 V8_INLINE int VisitAllocationSite(Map* map, AllocationSite* object); 914 V8_INLINE int VisitBytecodeArray(Map* map, BytecodeArray* object); 915 V8_INLINE int VisitCodeDataContainer(Map* map, CodeDataContainer* object); 916 V8_INLINE int VisitEphemeronHashTable(Map* map, EphemeronHashTable* object); 917 V8_INLINE int VisitFixedArray(Map* map, FixedArray* object); 918 V8_INLINE int VisitJSApiObject(Map* map, JSObject* object); 919 V8_INLINE int VisitJSFunction(Map* map, JSFunction* object); 920 V8_INLINE int VisitMap(Map* map, Map* object); 921 V8_INLINE int VisitNativeContext(Map* map, Context* object); 922 V8_INLINE int VisitTransitionArray(Map* map, TransitionArray* object); 923 924 // ObjectVisitor implementation. 925 V8_INLINE void VisitPointer(HeapObject* host, Object** p) final; 926 V8_INLINE void VisitPointer(HeapObject* host, MaybeObject** p) final; 927 V8_INLINE void VisitPointers(HeapObject* host, Object** start, 928 Object** end) final; 929 V8_INLINE void VisitPointers(HeapObject* host, MaybeObject** start, 930 MaybeObject** end) final; 931 V8_INLINE void VisitEmbeddedPointer(Code* host, RelocInfo* rinfo) final; 932 V8_INLINE void VisitCodeTarget(Code* host, RelocInfo* rinfo) final; 933 934 private: 935 // Granularity in which FixedArrays are scanned if |fixed_array_mode| 936 // is true. 937 static const int kProgressBarScanningChunk = 32 * 1024; 938 939 V8_INLINE int VisitFixedArrayIncremental(Map* map, FixedArray* object); 940 941 V8_INLINE void MarkMapContents(Map* map); 942 943 // Marks the object black without pushing it on the marking work list. Returns 944 // true if the object needed marking and false otherwise. 945 V8_INLINE bool MarkObjectWithoutPush(HeapObject* host, HeapObject* object); 946 947 // Marks the object grey and pushes it on the marking work list. 948 V8_INLINE void MarkObject(HeapObject* host, HeapObject* obj); 949 marking_state()950 MarkingState* marking_state() { return marking_state_; } 951 marking_worklist()952 MarkCompactCollector::MarkingWorklist* marking_worklist() const { 953 return collector_->marking_worklist(); 954 } 955 956 Heap* const heap_; 957 MarkCompactCollector* const collector_; 958 MarkingState* const marking_state_; 959 }; 960 961 class EvacuationScope { 962 public: EvacuationScope(MarkCompactCollector * collector)963 explicit EvacuationScope(MarkCompactCollector* collector) 964 : collector_(collector) { 965 collector_->set_evacuation(true); 966 } 967 ~EvacuationScope()968 ~EvacuationScope() { collector_->set_evacuation(false); } 969 970 private: 971 MarkCompactCollector* collector_; 972 }; 973 974 #ifdef ENABLE_MINOR_MC 975 976 // Collector for young-generation only. 977 class MinorMarkCompactCollector final : public MarkCompactCollectorBase { 978 public: 979 using MarkingState = MinorMarkingState; 980 using NonAtomicMarkingState = MinorNonAtomicMarkingState; 981 982 explicit MinorMarkCompactCollector(Heap* heap); 983 ~MinorMarkCompactCollector(); 984 marking_state()985 MarkingState* marking_state() { return &marking_state_; } 986 non_atomic_marking_state()987 NonAtomicMarkingState* non_atomic_marking_state() { 988 return &non_atomic_marking_state_; 989 } 990 991 void SetUp() override; 992 void TearDown() override; 993 void CollectGarbage() override; 994 995 void MakeIterable(Page* page, MarkingTreatmentMode marking_mode, 996 FreeSpaceTreatmentMode free_space_mode); 997 void CleanupSweepToIteratePages(); 998 999 private: 1000 using MarkingWorklist = Worklist<HeapObject*, 64 /* segment size */>; 1001 class RootMarkingVisitor; 1002 1003 static const int kNumMarkers = 8; 1004 static const int kMainMarker = 0; 1005 worklist()1006 inline MarkingWorklist* worklist() { return worklist_; } 1007 main_marking_visitor()1008 inline YoungGenerationMarkingVisitor* main_marking_visitor() { 1009 return main_marking_visitor_; 1010 } 1011 1012 void MarkLiveObjects() override; 1013 void MarkRootSetInParallel(RootMarkingVisitor* root_visitor); 1014 V8_INLINE void MarkRootObject(HeapObject* obj); 1015 void ProcessMarkingWorklist() override; 1016 void ClearNonLiveReferences() override; 1017 1018 void EvacuatePrologue() override; 1019 void EvacuateEpilogue() override; 1020 void Evacuate() override; 1021 void EvacuatePagesInParallel() override; 1022 void UpdatePointersAfterEvacuation() override; 1023 1024 UpdatingItem* CreateToSpaceUpdatingItem(MemoryChunk* chunk, Address start, 1025 Address end) override; 1026 UpdatingItem* CreateRememberedSetUpdatingItem( 1027 MemoryChunk* chunk, RememberedSetUpdatingMode updating_mode) override; 1028 1029 int CollectNewSpaceArrayBufferTrackerItems(ItemParallelJob* job); 1030 1031 int NumberOfParallelMarkingTasks(int pages); 1032 1033 MarkingWorklist* worklist_; 1034 1035 YoungGenerationMarkingVisitor* main_marking_visitor_; 1036 base::Semaphore page_parallel_job_semaphore_; 1037 std::vector<Page*> new_space_evacuation_pages_; 1038 std::vector<Page*> sweep_to_iterate_pages_; 1039 1040 MarkingState marking_state_; 1041 NonAtomicMarkingState non_atomic_marking_state_; 1042 1043 friend class YoungGenerationMarkingTask; 1044 friend class YoungGenerationMarkingVisitor; 1045 }; 1046 1047 #endif // ENABLE_MINOR_MC 1048 1049 } // namespace internal 1050 } // namespace v8 1051 1052 #endif // V8_HEAP_MARK_COMPACT_H_ 1053