xref: /third_party/node/deps/v8/src/heap/mark-compact.h (revision 1cb0ef41)
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 <atomic>
9#include <vector>
10
11#include "include/v8-internal.h"
12#include "src/heap/base/worklist.h"
13#include "src/heap/concurrent-marking.h"
14#include "src/heap/marking-visitor.h"
15#include "src/heap/marking-worklist.h"
16#include "src/heap/marking.h"
17#include "src/heap/memory-measurement.h"
18#include "src/heap/parallel-work-item.h"
19#include "src/heap/spaces.h"
20#include "src/heap/sweeper.h"
21
22namespace v8 {
23namespace internal {
24
25// Forward declarations.
26class EvacuationJobTraits;
27class HeapObjectVisitor;
28class ItemParallelJob;
29class LargePage;
30class MigrationObserver;
31class ReadOnlySpace;
32class RecordMigratedSlotVisitor;
33class UpdatingItem;
34class YoungGenerationMarkingVisitor;
35
36class MarkBitCellIterator {
37 public:
38  MarkBitCellIterator(const MemoryChunk* chunk, Bitmap* bitmap)
39      : chunk_(chunk) {
40    last_cell_index_ =
41        Bitmap::IndexToCell(chunk_->AddressToMarkbitIndex(chunk_->area_end()));
42    cell_base_ = chunk_->address();
43    cell_index_ =
44        Bitmap::IndexToCell(chunk_->AddressToMarkbitIndex(cell_base_));
45    cells_ = bitmap->cells();
46  }
47
48  inline bool Done() { return cell_index_ >= last_cell_index_; }
49
50  inline bool HasNext() { return cell_index_ < last_cell_index_ - 1; }
51
52  inline MarkBit::CellType* CurrentCell() {
53    DCHECK_EQ(cell_index_, Bitmap::IndexToCell(Bitmap::CellAlignIndex(
54                               chunk_->AddressToMarkbitIndex(cell_base_))));
55    return &cells_[cell_index_];
56  }
57
58  inline Address CurrentCellBase() {
59    DCHECK_EQ(cell_index_, Bitmap::IndexToCell(Bitmap::CellAlignIndex(
60                               chunk_->AddressToMarkbitIndex(cell_base_))));
61    return cell_base_;
62  }
63
64  V8_WARN_UNUSED_RESULT inline bool Advance() {
65    cell_base_ += Bitmap::kBitsPerCell * kTaggedSize;
66    return ++cell_index_ != last_cell_index_;
67  }
68
69  inline bool Advance(unsigned int new_cell_index) {
70    if (new_cell_index != cell_index_) {
71      DCHECK_GT(new_cell_index, cell_index_);
72      DCHECK_LE(new_cell_index, last_cell_index_);
73      unsigned int diff = new_cell_index - cell_index_;
74      cell_index_ = new_cell_index;
75      cell_base_ += diff * (Bitmap::kBitsPerCell * kTaggedSize);
76      return true;
77    }
78    return false;
79  }
80
81  // Return the next mark bit cell. If there is no next it returns 0;
82  inline MarkBit::CellType PeekNext() {
83    if (HasNext()) {
84      return cells_[cell_index_ + 1];
85    }
86    return 0;
87  }
88
89 private:
90  const MemoryChunk* chunk_;
91  MarkBit::CellType* cells_;
92  unsigned int last_cell_index_;
93  unsigned int cell_index_;
94  Address cell_base_;
95};
96
97enum LiveObjectIterationMode { kBlackObjects, kGreyObjects, kAllLiveObjects };
98
99template <LiveObjectIterationMode mode>
100class LiveObjectRange {
101 public:
102  class iterator {
103   public:
104    using value_type = std::pair<HeapObject, int /* size */>;
105    using pointer = const value_type*;
106    using reference = const value_type&;
107    using iterator_category = std::forward_iterator_tag;
108
109    inline iterator(const MemoryChunk* chunk, Bitmap* bitmap, Address start);
110
111    inline iterator& operator++();
112    inline iterator operator++(int);
113
114    bool operator==(iterator other) const {
115      return current_object_ == other.current_object_;
116    }
117
118    bool operator!=(iterator other) const { return !(*this == other); }
119
120    value_type operator*() {
121      return std::make_pair(current_object_, current_size_);
122    }
123
124   private:
125    inline void AdvanceToNextValidObject();
126
127    const MemoryChunk* const chunk_;
128    Map const one_word_filler_map_;
129    Map const two_word_filler_map_;
130    Map const free_space_map_;
131    MarkBitCellIterator it_;
132    Address cell_base_;
133    MarkBit::CellType current_cell_;
134    HeapObject current_object_;
135    int current_size_;
136  };
137
138  LiveObjectRange(const MemoryChunk* chunk, Bitmap* bitmap)
139      : chunk_(chunk),
140        bitmap_(bitmap),
141        start_(chunk_->area_start()),
142        end_(chunk->area_end()) {
143    DCHECK(!chunk->IsLargePage());
144  }
145
146  inline iterator begin();
147  inline iterator end();
148
149 private:
150  const MemoryChunk* const chunk_;
151  Bitmap* bitmap_;
152  Address start_;
153  Address end_;
154};
155
156class LiveObjectVisitor : AllStatic {
157 public:
158  enum IterationMode {
159    kKeepMarking,
160    kClearMarkbits,
161  };
162
163  // Visits black objects on a MemoryChunk until the Visitor returns |false| for
164  // an object. If IterationMode::kClearMarkbits is passed the markbits and
165  // slots for visited objects are cleared for each successfully visited object.
166  template <class Visitor, typename MarkingState>
167  static bool VisitBlackObjects(MemoryChunk* chunk, MarkingState* state,
168                                Visitor* visitor, IterationMode iteration_mode,
169                                HeapObject* failed_object);
170
171  // Visits black objects on a MemoryChunk. The visitor is not allowed to fail
172  // visitation for an object.
173  template <class Visitor, typename MarkingState>
174  static void VisitBlackObjectsNoFail(MemoryChunk* chunk, MarkingState* state,
175                                      Visitor* visitor,
176                                      IterationMode iteration_mode);
177
178  // Visits black objects on a MemoryChunk. The visitor is not allowed to fail
179  // visitation for an object.
180  template <class Visitor, typename MarkingState>
181  static void VisitGreyObjectsNoFail(MemoryChunk* chunk, MarkingState* state,
182                                     Visitor* visitor,
183                                     IterationMode iteration_mode);
184
185  template <typename MarkingState>
186  static void RecomputeLiveBytes(MemoryChunk* chunk, MarkingState* state);
187};
188
189enum class AlwaysPromoteYoung { kYes, kNo };
190enum PageEvacuationMode { NEW_TO_NEW, NEW_TO_OLD };
191enum class RememberedSetUpdatingMode { ALL, OLD_TO_NEW_ONLY };
192
193// Base class for minor and full MC collectors.
194class MarkCompactCollectorBase {
195 public:
196  virtual ~MarkCompactCollectorBase() = default;
197
198  virtual void SetUp() = 0;
199  virtual void TearDown() = 0;
200  virtual void CollectGarbage() = 0;
201
202  inline Heap* heap() const { return heap_; }
203  inline Isolate* isolate();
204
205 protected:
206  explicit MarkCompactCollectorBase(Heap* heap) : heap_(heap) {}
207
208  // Marking operations for objects reachable from roots.
209  virtual void MarkLiveObjects() = 0;
210  // Mark objects reachable (transitively) from objects in the marking
211  // work list.
212  virtual void DrainMarkingWorklist() = 0;
213  // Clear non-live references held in side data structures.
214  virtual void ClearNonLiveReferences() = 0;
215  virtual void EvacuatePrologue() = 0;
216  virtual void EvacuateEpilogue() = 0;
217  virtual void Evacuate() = 0;
218  virtual void EvacuatePagesInParallel() = 0;
219  virtual void UpdatePointersAfterEvacuation() = 0;
220  virtual std::unique_ptr<UpdatingItem> CreateRememberedSetUpdatingItem(
221      MemoryChunk* chunk, RememberedSetUpdatingMode updating_mode) = 0;
222
223  // Returns the number of wanted compaction tasks.
224  template <class Evacuator, class Collector>
225  size_t CreateAndExecuteEvacuationTasks(
226      Collector* collector,
227      std::vector<std::pair<ParallelWorkItem, MemoryChunk*>> evacuation_items,
228      MigrationObserver* migration_observer);
229
230  // Returns whether this page should be moved according to heuristics.
231  bool ShouldMovePage(Page* p, intptr_t live_bytes,
232                      AlwaysPromoteYoung promote_young);
233
234  template <typename IterateableSpace>
235  int CollectRememberedSetUpdatingItems(
236      std::vector<std::unique_ptr<UpdatingItem>>* items,
237      IterateableSpace* space, RememberedSetUpdatingMode mode);
238
239  int NumberOfParallelCompactionTasks();
240
241  Heap* heap_;
242};
243
244class MinorMarkingState final
245    : public MarkingStateBase<MinorMarkingState, AccessMode::ATOMIC> {
246 public:
247  explicit MinorMarkingState(PtrComprCageBase cage_base)
248      : MarkingStateBase(cage_base) {}
249
250  ConcurrentBitmap<AccessMode::ATOMIC>* bitmap(
251      const BasicMemoryChunk* chunk) const {
252    return MemoryChunk::cast(chunk)
253        ->young_generation_bitmap<AccessMode::ATOMIC>();
254  }
255
256  void IncrementLiveBytes(MemoryChunk* chunk, intptr_t by) {
257    chunk->young_generation_live_byte_count_ += by;
258  }
259
260  intptr_t live_bytes(MemoryChunk* chunk) const {
261    return chunk->young_generation_live_byte_count_;
262  }
263
264  void SetLiveBytes(MemoryChunk* chunk, intptr_t value) {
265    chunk->young_generation_live_byte_count_ = value;
266  }
267};
268
269class MinorNonAtomicMarkingState final
270    : public MarkingStateBase<MinorNonAtomicMarkingState,
271                              AccessMode::NON_ATOMIC> {
272 public:
273  explicit MinorNonAtomicMarkingState(PtrComprCageBase cage_base)
274      : MarkingStateBase(cage_base) {}
275
276  ConcurrentBitmap<AccessMode::NON_ATOMIC>* bitmap(
277      const BasicMemoryChunk* chunk) const {
278    return MemoryChunk::cast(chunk)
279        ->young_generation_bitmap<AccessMode::NON_ATOMIC>();
280  }
281
282  void IncrementLiveBytes(MemoryChunk* chunk, intptr_t by) {
283    chunk->young_generation_live_byte_count_.fetch_add(
284        by, std::memory_order_relaxed);
285  }
286
287  intptr_t live_bytes(MemoryChunk* chunk) const {
288    return chunk->young_generation_live_byte_count_.load(
289        std::memory_order_relaxed);
290  }
291
292  void SetLiveBytes(MemoryChunk* chunk, intptr_t value) {
293    chunk->young_generation_live_byte_count_.store(value,
294                                                   std::memory_order_relaxed);
295  }
296};
297
298// This is used by marking visitors.
299class MajorMarkingState final
300    : public MarkingStateBase<MajorMarkingState, AccessMode::ATOMIC> {
301 public:
302  explicit MajorMarkingState(PtrComprCageBase cage_base)
303      : MarkingStateBase(cage_base) {}
304
305  ConcurrentBitmap<AccessMode::ATOMIC>* bitmap(
306      const BasicMemoryChunk* chunk) const {
307    return chunk->marking_bitmap<AccessMode::ATOMIC>();
308  }
309
310  // Concurrent marking uses local live bytes so we may do these accesses
311  // non-atomically.
312  void IncrementLiveBytes(MemoryChunk* chunk, intptr_t by) {
313    chunk->live_byte_count_.fetch_add(by, std::memory_order_relaxed);
314  }
315
316  intptr_t live_bytes(MemoryChunk* chunk) const {
317    return chunk->live_byte_count_.load(std::memory_order_relaxed);
318  }
319
320  void SetLiveBytes(MemoryChunk* chunk, intptr_t value) {
321    chunk->live_byte_count_.store(value, std::memory_order_relaxed);
322  }
323};
324
325// This is used by Scavenger and Evacuator in TransferColor.
326// Live byte increments have to be atomic.
327class MajorAtomicMarkingState final
328    : public MarkingStateBase<MajorAtomicMarkingState, AccessMode::ATOMIC> {
329 public:
330  explicit MajorAtomicMarkingState(PtrComprCageBase cage_base)
331      : MarkingStateBase(cage_base) {}
332
333  ConcurrentBitmap<AccessMode::ATOMIC>* bitmap(
334      const BasicMemoryChunk* chunk) const {
335    return chunk->marking_bitmap<AccessMode::ATOMIC>();
336  }
337
338  void IncrementLiveBytes(MemoryChunk* chunk, intptr_t by) {
339    chunk->live_byte_count_.fetch_add(by);
340  }
341};
342
343class MajorNonAtomicMarkingState final
344    : public MarkingStateBase<MajorNonAtomicMarkingState,
345                              AccessMode::NON_ATOMIC> {
346 public:
347  explicit MajorNonAtomicMarkingState(PtrComprCageBase cage_base)
348      : MarkingStateBase(cage_base) {}
349
350  ConcurrentBitmap<AccessMode::NON_ATOMIC>* bitmap(
351      const BasicMemoryChunk* chunk) const {
352    return chunk->marking_bitmap<AccessMode::NON_ATOMIC>();
353  }
354
355  void IncrementLiveBytes(MemoryChunk* chunk, intptr_t by) {
356    chunk->live_byte_count_.fetch_add(by, std::memory_order_relaxed);
357  }
358
359  intptr_t live_bytes(MemoryChunk* chunk) const {
360    return chunk->live_byte_count_.load(std::memory_order_relaxed);
361  }
362
363  void SetLiveBytes(MemoryChunk* chunk, intptr_t value) {
364    chunk->live_byte_count_.store(value, std::memory_order_relaxed);
365  }
366};
367
368// This visitor is used for marking on the main thread. It is cheaper than
369// the concurrent marking visitor because it does not snapshot JSObjects.
370template <typename MarkingState>
371class MainMarkingVisitor final
372    : public MarkingVisitorBase<MainMarkingVisitor<MarkingState>,
373                                MarkingState> {
374 public:
375  // This is used for revisiting objects that were black allocated.
376  class V8_NODISCARD RevisitScope {
377   public:
378    explicit RevisitScope(MainMarkingVisitor* visitor) : visitor_(visitor) {
379      DCHECK(!visitor->revisiting_object_);
380      visitor->revisiting_object_ = true;
381    }
382    ~RevisitScope() {
383      DCHECK(visitor_->revisiting_object_);
384      visitor_->revisiting_object_ = false;
385    }
386
387   private:
388    MainMarkingVisitor<MarkingState>* visitor_;
389  };
390
391  MainMarkingVisitor(MarkingState* marking_state,
392                     MarkingWorklists::Local* local_marking_worklists,
393                     WeakObjects::Local* local_weak_objects, Heap* heap,
394                     unsigned mark_compact_epoch,
395                     base::EnumSet<CodeFlushMode> code_flush_mode,
396                     bool embedder_tracing_enabled,
397                     bool should_keep_ages_unchanged)
398      : MarkingVisitorBase<MainMarkingVisitor<MarkingState>, MarkingState>(
399            local_marking_worklists, local_weak_objects, heap,
400            mark_compact_epoch, code_flush_mode, embedder_tracing_enabled,
401            should_keep_ages_unchanged),
402        marking_state_(marking_state),
403        revisiting_object_(false) {}
404
405  // HeapVisitor override to allow revisiting of black objects.
406  bool ShouldVisit(HeapObject object) {
407    return marking_state_->GreyToBlack(object) ||
408           V8_UNLIKELY(revisiting_object_);
409  }
410
411 private:
412  // Functions required by MarkingVisitorBase.
413
414  template <typename T, typename TBodyDescriptor = typename T::BodyDescriptor>
415  int VisitJSObjectSubclass(Map map, T object);
416
417  template <typename T>
418  int VisitLeftTrimmableArray(Map map, T object);
419
420  template <typename TSlot>
421  void RecordSlot(HeapObject object, TSlot slot, HeapObject target);
422
423  void RecordRelocSlot(Code host, RelocInfo* rinfo, HeapObject target);
424
425  void SynchronizePageAccess(HeapObject heap_object) {
426    // Nothing to do on the main thread.
427  }
428
429  MarkingState* marking_state() { return marking_state_; }
430
431  TraceRetainingPathMode retaining_path_mode() {
432    return (V8_UNLIKELY(FLAG_track_retaining_path))
433               ? TraceRetainingPathMode::kEnabled
434               : TraceRetainingPathMode::kDisabled;
435  }
436
437  MarkingState* const marking_state_;
438
439  friend class MarkingVisitorBase<MainMarkingVisitor<MarkingState>,
440                                  MarkingState>;
441  bool revisiting_object_;
442};
443
444// Collector for young and old generation.
445class MarkCompactCollector final : public MarkCompactCollectorBase {
446 public:
447  using MarkingState = MajorMarkingState;
448  using AtomicMarkingState = MajorAtomicMarkingState;
449  using NonAtomicMarkingState = MajorNonAtomicMarkingState;
450
451  using MarkingVisitor = MainMarkingVisitor<MarkingState>;
452
453  class RootMarkingVisitor;
454  class CustomRootBodyMarkingVisitor;
455  class SharedHeapObjectVisitor;
456
457  enum IterationMode {
458    kKeepMarking,
459    kClearMarkbits,
460  };
461
462  enum class MarkingWorklistProcessingMode {
463    kDefault,
464    kTrackNewlyDiscoveredObjects
465  };
466
467  enum class StartCompactionMode {
468    kIncremental,
469    kAtomic,
470  };
471
472  MarkingState* marking_state() { return &marking_state_; }
473
474  NonAtomicMarkingState* non_atomic_marking_state() {
475    return &non_atomic_marking_state_;
476  }
477
478  void SetUp() override;
479  void TearDown() override;
480  // Performs a global garbage collection.
481  void CollectGarbage() override;
482
483  void CollectEvacuationCandidates(PagedSpace* space);
484
485  void AddEvacuationCandidate(Page* p);
486
487  // Prepares for GC by resetting relocation info in old and map spaces and
488  // choosing spaces to compact.
489  void Prepare();
490
491  // Stop concurrent marking (either by preempting it right away or waiting for
492  // it to complete as requested by |stop_request|).
493  void FinishConcurrentMarking();
494
495  // Returns whether compaction is running.
496  bool StartCompaction(StartCompactionMode mode);
497
498  void AbortCompaction();
499
500  void StartMarking();
501
502  static inline bool IsOnEvacuationCandidate(Object obj) {
503    return Page::FromAddress(obj.ptr())->IsEvacuationCandidate();
504  }
505
506  static bool IsOnEvacuationCandidate(MaybeObject obj);
507
508  struct RecordRelocSlotInfo {
509    MemoryChunk* memory_chunk;
510    SlotType slot_type;
511    uint32_t offset;
512  };
513
514  static V8_EXPORT_PRIVATE bool IsMapOrForwardedMap(Map map);
515
516  static bool ShouldRecordRelocSlot(Code host, RelocInfo* rinfo,
517                                    HeapObject target);
518  static RecordRelocSlotInfo ProcessRelocInfo(Code host, RelocInfo* rinfo,
519                                              HeapObject target);
520
521  static void RecordRelocSlot(Code host, RelocInfo* rinfo, HeapObject target);
522  V8_INLINE static void RecordSlot(HeapObject object, ObjectSlot slot,
523                                   HeapObject target);
524  V8_INLINE static void RecordSlot(HeapObject object, HeapObjectSlot slot,
525                                   HeapObject target);
526  V8_INLINE static void RecordSlot(MemoryChunk* source_page,
527                                   HeapObjectSlot slot, HeapObject target);
528  void RecordLiveSlotsOnPage(Page* page);
529
530  bool is_compacting() const { return compacting_; }
531  bool is_shared_heap() const { return is_shared_heap_; }
532
533  void FinishSweepingIfOutOfWork();
534
535  enum class SweepingForcedFinalizationMode { kUnifiedHeap, kV8Only };
536
537  // Ensures that sweeping is finished.
538  //
539  // Note: Can only be called safely from main thread.
540  V8_EXPORT_PRIVATE void EnsureSweepingCompleted(
541      SweepingForcedFinalizationMode mode);
542
543  void EnsurePageIsSwept(Page* page);
544
545  void DrainSweepingWorklistForSpace(AllocationSpace space);
546
547  // Checks if sweeping is in progress right now on any space.
548  bool sweeping_in_progress() const { return sweeper_->sweeping_in_progress(); }
549
550  void set_evacuation(bool evacuation) { evacuation_ = evacuation; }
551
552  bool evacuation() const { return evacuation_; }
553
554  MarkingWorklists* marking_worklists() { return &marking_worklists_; }
555
556  MarkingWorklists::Local* local_marking_worklists() {
557    return local_marking_worklists_.get();
558  }
559
560  WeakObjects* weak_objects() { return &weak_objects_; }
561
562  WeakObjects::Local* local_weak_objects() { return local_weak_objects_.get(); }
563
564  inline void AddTransitionArray(TransitionArray array);
565
566  void AddNewlyDiscovered(HeapObject object) {
567    if (ephemeron_marking_.newly_discovered_overflowed) return;
568
569    if (ephemeron_marking_.newly_discovered.size() <
570        ephemeron_marking_.newly_discovered_limit) {
571      ephemeron_marking_.newly_discovered.push_back(object);
572    } else {
573      ephemeron_marking_.newly_discovered_overflowed = true;
574    }
575  }
576
577  void ResetNewlyDiscovered() {
578    ephemeron_marking_.newly_discovered_overflowed = false;
579    ephemeron_marking_.newly_discovered.clear();
580  }
581
582  Sweeper* sweeper() { return sweeper_; }
583
584#ifdef DEBUG
585  // Checks whether performing mark-compact collection.
586  bool in_use() { return state_ > PREPARE_GC; }
587  bool are_map_pointers_encoded() { return state_ == UPDATE_POINTERS; }
588#endif
589
590  void VerifyMarking();
591#ifdef VERIFY_HEAP
592  void VerifyMarkbitsAreClean();
593  void VerifyMarkbitsAreDirty(ReadOnlySpace* space);
594  void VerifyMarkbitsAreClean(PagedSpace* space);
595  void VerifyMarkbitsAreClean(NewSpace* space);
596  void VerifyMarkbitsAreClean(LargeObjectSpace* space);
597#endif
598
599  unsigned epoch() const { return epoch_; }
600
601  base::EnumSet<CodeFlushMode> code_flush_mode() const {
602    return code_flush_mode_;
603  }
604
605  explicit MarkCompactCollector(Heap* heap);
606  ~MarkCompactCollector() override;
607
608  // Used by wrapper tracing.
609  V8_INLINE void MarkExternallyReferencedObject(HeapObject obj);
610  // Used by incremental marking for object that change their layout.
611  void VisitObject(HeapObject obj);
612  // Used by incremental marking for black-allocated objects.
613  void RevisitObject(HeapObject obj);
614
615  // Drains the main thread marking worklist until the specified number of
616  // bytes are processed. If the number of bytes is zero, then the worklist
617  // is drained until it is empty.
618  template <MarkingWorklistProcessingMode mode =
619                MarkingWorklistProcessingMode::kDefault>
620  std::pair<size_t, size_t> ProcessMarkingWorklist(size_t bytes_to_process);
621
622 private:
623  void ComputeEvacuationHeuristics(size_t area_size,
624                                   int* target_fragmentation_percent,
625                                   size_t* max_evacuated_bytes);
626
627  void RecordObjectStats();
628
629  // Finishes GC, performs heap verification if enabled.
630  void Finish();
631
632  // Free unmarked ArrayBufferExtensions.
633  void SweepArrayBufferExtensions();
634
635  // Free unmarked entries in the ExternalPointerTable.
636  void SweepExternalPointerTable();
637
638  void MarkLiveObjects() override;
639
640  // Marks the object grey and adds it to the marking work list.
641  // This is for non-incremental marking only.
642  V8_INLINE void MarkObject(HeapObject host, HeapObject obj);
643
644  // Marks the object grey and adds it to the marking work list.
645  // This is for non-incremental marking only.
646  V8_INLINE void MarkRootObject(Root root, HeapObject obj);
647
648  // Mark the heap roots and all objects reachable from them.
649  void MarkRoots(RootVisitor* root_visitor,
650                 ObjectVisitor* custom_root_body_visitor);
651
652  // Mark all objects that are directly referenced from one of the clients
653  // heaps.
654  void MarkObjectsFromClientHeaps();
655
656  // Updates pointers to shared objects from client heaps.
657  void UpdatePointersInClientHeaps();
658  void UpdatePointersInClientHeap(Isolate* client);
659
660  // Marks object reachable from harmony weak maps and wrapper tracing.
661  void ProcessEphemeronMarking();
662
663  // If the call-site of the top optimized code was not prepared for
664  // deoptimization, then treat embedded pointers in the code as strong as
665  // otherwise they can die and try to deoptimize the underlying code.
666  void ProcessTopOptimizedFrame(ObjectVisitor* visitor, Isolate* isolate);
667
668  // Drains the main thread marking work list. Will mark all pending objects
669  // if no concurrent threads are running.
670  void DrainMarkingWorklist() override;
671
672  // Implements ephemeron semantics: Marks value if key is already reachable.
673  // Returns true if value was actually marked.
674  bool ProcessEphemeron(HeapObject key, HeapObject value);
675
676  // Marks ephemerons and drains marking worklist iteratively
677  // until a fixpoint is reached. Returns false if too many iterations have been
678  // tried and the linear approach should be used.
679  bool ProcessEphemeronsUntilFixpoint();
680
681  // Drains ephemeron and marking worklists. Single iteration of the
682  // fixpoint iteration.
683  bool ProcessEphemerons();
684
685  // Mark ephemerons and drain marking worklist with a linear algorithm.
686  // Only used if fixpoint iteration doesn't finish within a few iterations.
687  void ProcessEphemeronsLinear();
688
689  // Perform Wrapper Tracing if in use.
690  void PerformWrapperTracing();
691
692  // Callback function for telling whether the object *p is an unmarked
693  // heap object.
694  static bool IsUnmarkedHeapObject(Heap* heap, FullObjectSlot p);
695
696  // Clear non-live references in weak cells, transition and descriptor arrays,
697  // and deoptimize dependent code of non-live maps.
698  void ClearNonLiveReferences() override;
699  void MarkDependentCodeForDeoptimization();
700  // Checks if the given weak cell is a simple transition from the parent map
701  // of the given dead target. If so it clears the transition and trims
702  // the descriptor array of the parent if needed.
703  void ClearPotentialSimpleMapTransition(Map dead_target);
704  void ClearPotentialSimpleMapTransition(Map map, Map dead_target);
705
706  // Flushes a weakly held bytecode array from a shared function info.
707  void FlushBytecodeFromSFI(SharedFunctionInfo shared_info);
708
709  // Clears bytecode arrays / baseline code that have not been executed for
710  // multiple collections.
711  void ProcessOldCodeCandidates();
712  void ProcessFlushedBaselineCandidates();
713
714  // Resets any JSFunctions which have had their bytecode flushed.
715  void ClearFlushedJsFunctions();
716
717  // Compact every array in the global list of transition arrays and
718  // trim the corresponding descriptor array if a transition target is non-live.
719  void ClearFullMapTransitions();
720  void TrimDescriptorArray(Map map, DescriptorArray descriptors);
721  void TrimEnumCache(Map map, DescriptorArray descriptors);
722  bool CompactTransitionArray(Map map, TransitionArray transitions,
723                              DescriptorArray descriptors);
724  bool TransitionArrayNeedsCompaction(TransitionArray transitions,
725                                      int num_transitions);
726
727  // After all reachable objects have been marked those weak map entries
728  // with an unreachable key are removed from all encountered weak maps.
729  // The linked list of all encountered weak maps is destroyed.
730  void ClearWeakCollections();
731
732  // Goes through the list of encountered weak references and clears those with
733  // dead values. If the value is a dead map and the parent map transitions to
734  // the dead map via weak cell, then this function also clears the map
735  // transition.
736  void ClearWeakReferences();
737
738  // Goes through the list of encountered JSWeakRefs and WeakCells and clears
739  // those with dead values.
740  void ClearJSWeakRefs();
741
742  // Starts sweeping of spaces by contributing on the main thread and setting
743  // up other pages for sweeping. Does not start sweeper tasks.
744  void StartSweepSpaces();
745  void StartSweepSpace(PagedSpace* space);
746
747  void EvacuatePrologue() override;
748  void EvacuateEpilogue() override;
749  void Evacuate() override;
750  void EvacuatePagesInParallel() override;
751  void UpdatePointersAfterEvacuation() override;
752
753  std::unique_ptr<UpdatingItem> CreateRememberedSetUpdatingItem(
754      MemoryChunk* chunk, RememberedSetUpdatingMode updating_mode) override;
755
756  void ReleaseEvacuationCandidates();
757  // Returns number of aborted pages.
758  size_t PostProcessEvacuationCandidates();
759  void ReportAbortedEvacuationCandidateDueToOOM(Address failed_start,
760                                                Page* page);
761  void ReportAbortedEvacuationCandidateDueToFlags(Address failed_start,
762                                                  Page* page);
763
764  static const int kEphemeronChunkSize = 8 * KB;
765
766  int NumberOfParallelEphemeronVisitingTasks(size_t elements);
767
768  void RightTrimDescriptorArray(DescriptorArray array, int descriptors_to_trim);
769
770  base::Mutex mutex_;
771  base::Semaphore page_parallel_job_semaphore_{0};
772
773#ifdef DEBUG
774  enum CollectorState{IDLE,
775                      PREPARE_GC,
776                      MARK_LIVE_OBJECTS,
777                      SWEEP_SPACES,
778                      ENCODE_FORWARDING_ADDRESSES,
779                      UPDATE_POINTERS,
780                      RELOCATE_OBJECTS};
781
782  // The current stage of the collector.
783  CollectorState state_;
784#endif
785
786  const bool is_shared_heap_;
787
788  bool evacuation_ = false;
789  // True if we are collecting slots to perform evacuation from evacuation
790  // candidates.
791  bool compacting_ = false;
792  bool black_allocation_ = false;
793  bool have_code_to_deoptimize_ = false;
794
795  MarkingWorklists marking_worklists_;
796
797  WeakObjects weak_objects_;
798  EphemeronMarking ephemeron_marking_;
799
800  std::unique_ptr<MarkingVisitor> marking_visitor_;
801  std::unique_ptr<MarkingWorklists::Local> local_marking_worklists_;
802  std::unique_ptr<WeakObjects::Local> local_weak_objects_;
803  NativeContextInferrer native_context_inferrer_;
804  NativeContextStats native_context_stats_;
805
806  // Candidates for pages that should be evacuated.
807  std::vector<Page*> evacuation_candidates_;
808  // Pages that are actually processed during evacuation.
809  std::vector<Page*> old_space_evacuation_pages_;
810  std::vector<Page*> new_space_evacuation_pages_;
811  std::vector<std::pair<Address, Page*>>
812      aborted_evacuation_candidates_due_to_oom_;
813  std::vector<std::pair<Address, Page*>>
814      aborted_evacuation_candidates_due_to_flags_;
815  std::vector<LargePage*> promoted_large_pages_;
816
817  MarkingState marking_state_;
818  NonAtomicMarkingState non_atomic_marking_state_;
819
820  Sweeper* sweeper_;
821
822  // Counts the number of major mark-compact collections. The counter is
823  // incremented right after marking. This is used for:
824  // - marking descriptor arrays. See NumberOfMarkedDescriptors. Only the lower
825  //   two bits are used, so it is okay if this counter overflows and wraps
826  //   around.
827  unsigned epoch_ = 0;
828
829  // Bytecode flushing is disabled when the code coverage mode is changed. Since
830  // that can happen while a GC is happening and we need the
831  // code_flush_mode_ to remain the same through out a GC, we record this at
832  // the start of each GC.
833  base::EnumSet<CodeFlushMode> code_flush_mode_;
834
835  friend class FullEvacuator;
836  friend class RecordMigratedSlotVisitor;
837};
838
839class V8_NODISCARD EvacuationScope {
840 public:
841  explicit EvacuationScope(MarkCompactCollector* collector)
842      : collector_(collector) {
843    collector_->set_evacuation(true);
844  }
845
846  ~EvacuationScope() { collector_->set_evacuation(false); }
847
848 private:
849  MarkCompactCollector* collector_;
850};
851
852// Collector for young-generation only.
853class MinorMarkCompactCollector final : public MarkCompactCollectorBase {
854 public:
855  using MarkingState = MinorMarkingState;
856  using NonAtomicMarkingState = MinorNonAtomicMarkingState;
857
858  static constexpr size_t kMaxParallelTasks = 8;
859
860  explicit MinorMarkCompactCollector(Heap* heap);
861  ~MinorMarkCompactCollector() override;
862
863  MarkingState* marking_state() { return &marking_state_; }
864
865  NonAtomicMarkingState* non_atomic_marking_state() {
866    return &non_atomic_marking_state_;
867  }
868
869  void SetUp() override;
870  void TearDown() override;
871  void CollectGarbage() override;
872
873  void MakeIterable(Page* page, FreeSpaceTreatmentMode free_space_mode);
874  void CleanupPromotedPages();
875
876 private:
877  using MarkingWorklist =
878      ::heap::base::Worklist<HeapObject, 64 /* segment size */>;
879  class RootMarkingVisitor;
880
881  static const int kNumMarkers = 8;
882  static const int kMainMarker = 0;
883
884  inline MarkingWorklist* worklist() { return worklist_; }
885
886  inline YoungGenerationMarkingVisitor* main_marking_visitor() {
887    return main_marking_visitor_;
888  }
889
890  void MarkLiveObjects() override;
891  void MarkRootSetInParallel(RootMarkingVisitor* root_visitor);
892  V8_INLINE void MarkRootObject(HeapObject obj);
893  void DrainMarkingWorklist() override;
894  void TraceFragmentation();
895  void ClearNonLiveReferences() override;
896
897  void EvacuatePrologue() override;
898  void EvacuateEpilogue() override;
899  void Evacuate() override;
900  void EvacuatePagesInParallel() override;
901  void UpdatePointersAfterEvacuation() override;
902
903  std::unique_ptr<UpdatingItem> CreateToSpaceUpdatingItem(MemoryChunk* chunk,
904                                                          Address start,
905                                                          Address end);
906  std::unique_ptr<UpdatingItem> CreateRememberedSetUpdatingItem(
907      MemoryChunk* chunk, RememberedSetUpdatingMode updating_mode) override;
908
909  int CollectToSpaceUpdatingItems(
910      std::vector<std::unique_ptr<UpdatingItem>>* items);
911
912  void SweepArrayBufferExtensions();
913
914  MarkingWorklist* worklist_;
915  MarkingWorklist::Local main_thread_worklist_local_;
916
917  MarkingState marking_state_;
918  NonAtomicMarkingState non_atomic_marking_state_;
919
920  YoungGenerationMarkingVisitor* main_marking_visitor_;
921  base::Semaphore page_parallel_job_semaphore_;
922  std::vector<Page*> new_space_evacuation_pages_;
923  std::vector<Page*> promoted_pages_;
924  std::vector<LargePage*> promoted_large_pages_;
925
926  friend class YoungGenerationMarkingTask;
927  friend class YoungGenerationMarkingJob;
928  friend class YoungGenerationMarkingVisitor;
929};
930
931}  // namespace internal
932}  // namespace v8
933
934#endif  // V8_HEAP_MARK_COMPACT_H_
935