Home | History | Annotate | Download | only in heap
      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/incremental-marking.h"
      6 
      7 #include "src/code-stubs.h"
      8 #include "src/compilation-cache.h"
      9 #include "src/conversions.h"
     10 #include "src/heap/gc-idle-time-handler.h"
     11 #include "src/heap/gc-tracer.h"
     12 #include "src/heap/heap-inl.h"
     13 #include "src/heap/mark-compact-inl.h"
     14 #include "src/heap/object-stats.h"
     15 #include "src/heap/objects-visiting-inl.h"
     16 #include "src/heap/objects-visiting.h"
     17 #include "src/tracing/trace-event.h"
     18 #include "src/v8.h"
     19 
     20 namespace v8 {
     21 namespace internal {
     22 
     23 IncrementalMarking::IncrementalMarking(Heap* heap)
     24     : heap_(heap),
     25       state_(STOPPED),
     26       initial_old_generation_size_(0),
     27       bytes_marked_ahead_of_schedule_(0),
     28       unscanned_bytes_of_large_object_(0),
     29       idle_marking_delay_counter_(0),
     30       incremental_marking_finalization_rounds_(0),
     31       is_compacting_(false),
     32       should_hurry_(false),
     33       was_activated_(false),
     34       black_allocation_(false),
     35       finalize_marking_completed_(false),
     36       trace_wrappers_toggle_(false),
     37       request_type_(NONE),
     38       new_generation_observer_(*this, kAllocatedThreshold),
     39       old_generation_observer_(*this, kAllocatedThreshold) {}
     40 
     41 bool IncrementalMarking::BaseRecordWrite(HeapObject* obj, Object* value) {
     42   HeapObject* value_heap_obj = HeapObject::cast(value);
     43   DCHECK(!ObjectMarking::IsImpossible(value_heap_obj));
     44   DCHECK(!ObjectMarking::IsImpossible(obj));
     45   const bool is_black = ObjectMarking::IsBlack(obj);
     46 
     47   if (is_black && ObjectMarking::IsWhite(value_heap_obj)) {
     48     WhiteToGreyAndPush(value_heap_obj);
     49     RestartIfNotMarking();
     50   }
     51   return is_compacting_ && is_black;
     52 }
     53 
     54 
     55 void IncrementalMarking::RecordWriteSlow(HeapObject* obj, Object** slot,
     56                                          Object* value) {
     57   if (BaseRecordWrite(obj, value) && slot != NULL) {
     58     // Object is not going to be rescanned we need to record the slot.
     59     heap_->mark_compact_collector()->RecordSlot(obj, slot, value);
     60   }
     61 }
     62 
     63 
     64 void IncrementalMarking::RecordWriteFromCode(HeapObject* obj, Object** slot,
     65                                              Isolate* isolate) {
     66   DCHECK(obj->IsHeapObject());
     67   isolate->heap()->incremental_marking()->RecordWrite(obj, slot, *slot);
     68 }
     69 
     70 // static
     71 void IncrementalMarking::RecordWriteOfCodeEntryFromCode(JSFunction* host,
     72                                                         Object** slot,
     73                                                         Isolate* isolate) {
     74   DCHECK(host->IsJSFunction());
     75   IncrementalMarking* marking = isolate->heap()->incremental_marking();
     76   Code* value = Code::cast(
     77       Code::GetObjectFromEntryAddress(reinterpret_cast<Address>(slot)));
     78   marking->RecordWriteOfCodeEntry(host, slot, value);
     79 }
     80 
     81 void IncrementalMarking::RecordCodeTargetPatch(Code* host, Address pc,
     82                                                HeapObject* value) {
     83   if (IsMarking()) {
     84     RelocInfo rinfo(heap_->isolate(), pc, RelocInfo::CODE_TARGET, 0, host);
     85     RecordWriteIntoCode(host, &rinfo, value);
     86   }
     87 }
     88 
     89 
     90 void IncrementalMarking::RecordCodeTargetPatch(Address pc, HeapObject* value) {
     91   if (IsMarking()) {
     92     Code* host = heap_->isolate()
     93                      ->inner_pointer_to_code_cache()
     94                      ->GcSafeFindCodeForInnerPointer(pc);
     95     RelocInfo rinfo(heap_->isolate(), pc, RelocInfo::CODE_TARGET, 0, host);
     96     RecordWriteIntoCode(host, &rinfo, value);
     97   }
     98 }
     99 
    100 
    101 void IncrementalMarking::RecordWriteOfCodeEntrySlow(JSFunction* host,
    102                                                     Object** slot,
    103                                                     Code* value) {
    104   if (BaseRecordWrite(host, value)) {
    105     DCHECK(slot != NULL);
    106     heap_->mark_compact_collector()->RecordCodeEntrySlot(
    107         host, reinterpret_cast<Address>(slot), value);
    108   }
    109 }
    110 
    111 void IncrementalMarking::RecordWriteIntoCodeSlow(Code* host, RelocInfo* rinfo,
    112                                                  Object* value) {
    113   if (BaseRecordWrite(host, value)) {
    114     // Object is not going to be rescanned.  We need to record the slot.
    115     heap_->mark_compact_collector()->RecordRelocSlot(host, rinfo, value);
    116   }
    117 }
    118 
    119 void IncrementalMarking::WhiteToGreyAndPush(HeapObject* obj) {
    120   ObjectMarking::WhiteToGrey(obj);
    121   heap_->mark_compact_collector()->marking_deque()->Push(obj);
    122 }
    123 
    124 
    125 static void MarkObjectGreyDoNotEnqueue(Object* obj) {
    126   if (obj->IsHeapObject()) {
    127     HeapObject* heap_obj = HeapObject::cast(obj);
    128     ObjectMarking::AnyToGrey(heap_obj);
    129   }
    130 }
    131 
    132 void IncrementalMarking::TransferMark(Heap* heap, HeapObject* from,
    133                                       HeapObject* to) {
    134   DCHECK(MemoryChunk::FromAddress(from->address())->SweepingDone());
    135   // This is only used when resizing an object.
    136   DCHECK(MemoryChunk::FromAddress(from->address()) ==
    137          MemoryChunk::FromAddress(to->address()));
    138 
    139   if (!heap->incremental_marking()->IsMarking()) return;
    140 
    141   // If the mark doesn't move, we don't check the color of the object.
    142   // It doesn't matter whether the object is black, since it hasn't changed
    143   // size, so the adjustment to the live data count will be zero anyway.
    144   if (from == to) return;
    145 
    146   MarkBit new_mark_bit = ObjectMarking::MarkBitFrom(to);
    147   MarkBit old_mark_bit = ObjectMarking::MarkBitFrom(from);
    148 
    149 #ifdef DEBUG
    150   Marking::ObjectColor old_color = Marking::Color(old_mark_bit);
    151 #endif
    152 
    153   if (Marking::IsBlack(old_mark_bit)) {
    154     Marking::BlackToWhite(old_mark_bit);
    155     Marking::WhiteToBlack(new_mark_bit);
    156     return;
    157   } else if (Marking::IsGrey(old_mark_bit)) {
    158     Marking::GreyToWhite(old_mark_bit);
    159     Marking::WhiteToGrey(new_mark_bit);
    160     heap->mark_compact_collector()->marking_deque()->Push(to);
    161     heap->incremental_marking()->RestartIfNotMarking();
    162   }
    163 
    164 #ifdef DEBUG
    165   Marking::ObjectColor new_color = Marking::Color(new_mark_bit);
    166   DCHECK(new_color == old_color);
    167 #endif
    168 }
    169 
    170 class IncrementalMarkingMarkingVisitor
    171     : public StaticMarkingVisitor<IncrementalMarkingMarkingVisitor> {
    172  public:
    173   static void Initialize() {
    174     StaticMarkingVisitor<IncrementalMarkingMarkingVisitor>::Initialize();
    175     table_.Register(kVisitFixedArray, &VisitFixedArrayIncremental);
    176     table_.Register(kVisitNativeContext, &VisitNativeContextIncremental);
    177   }
    178 
    179   static const int kProgressBarScanningChunk = 32 * 1024;
    180 
    181   static void VisitFixedArrayIncremental(Map* map, HeapObject* object) {
    182     MemoryChunk* chunk = MemoryChunk::FromAddress(object->address());
    183     if (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR)) {
    184       DCHECK(!FLAG_use_marking_progress_bar ||
    185              chunk->owner()->identity() == LO_SPACE);
    186       Heap* heap = map->GetHeap();
    187       // When using a progress bar for large fixed arrays, scan only a chunk of
    188       // the array and try to push it onto the marking deque again until it is
    189       // fully scanned. Fall back to scanning it through to the end in case this
    190       // fails because of a full deque.
    191       int object_size = FixedArray::BodyDescriptor::SizeOf(map, object);
    192       int start_offset =
    193           Max(FixedArray::BodyDescriptor::kStartOffset, chunk->progress_bar());
    194       int end_offset =
    195           Min(object_size, start_offset + kProgressBarScanningChunk);
    196       int already_scanned_offset = start_offset;
    197       bool scan_until_end = false;
    198       do {
    199         VisitPointers(heap, object, HeapObject::RawField(object, start_offset),
    200                       HeapObject::RawField(object, end_offset));
    201         start_offset = end_offset;
    202         end_offset = Min(object_size, end_offset + kProgressBarScanningChunk);
    203         scan_until_end =
    204             heap->mark_compact_collector()->marking_deque()->IsFull();
    205       } while (scan_until_end && start_offset < object_size);
    206       chunk->set_progress_bar(start_offset);
    207       if (start_offset < object_size) {
    208         if (ObjectMarking::IsGrey(object)) {
    209           heap->mark_compact_collector()->marking_deque()->Unshift(object);
    210         } else {
    211           DCHECK(ObjectMarking::IsBlack(object));
    212           heap->mark_compact_collector()->UnshiftBlack(object);
    213         }
    214         heap->incremental_marking()->NotifyIncompleteScanOfObject(
    215             object_size - (start_offset - already_scanned_offset));
    216       }
    217     } else {
    218       FixedArrayVisitor::Visit(map, object);
    219     }
    220   }
    221 
    222   static void VisitNativeContextIncremental(Map* map, HeapObject* object) {
    223     Context* context = Context::cast(object);
    224 
    225     // We will mark cache black with a separate pass when we finish marking.
    226     // Note that GC can happen when the context is not fully initialized,
    227     // so the cache can be undefined.
    228     Object* cache = context->get(Context::NORMALIZED_MAP_CACHE_INDEX);
    229     if (!cache->IsUndefined(map->GetIsolate())) {
    230       MarkObjectGreyDoNotEnqueue(cache);
    231     }
    232     VisitNativeContext(map, context);
    233   }
    234 
    235   INLINE(static void VisitPointer(Heap* heap, HeapObject* object, Object** p)) {
    236     Object* target = *p;
    237     if (target->IsHeapObject()) {
    238       heap->mark_compact_collector()->RecordSlot(object, p, target);
    239       MarkObject(heap, target);
    240     }
    241   }
    242 
    243   INLINE(static void VisitPointers(Heap* heap, HeapObject* object,
    244                                    Object** start, Object** end)) {
    245     for (Object** p = start; p < end; p++) {
    246       Object* target = *p;
    247       if (target->IsHeapObject()) {
    248         heap->mark_compact_collector()->RecordSlot(object, p, target);
    249         MarkObject(heap, target);
    250       }
    251     }
    252   }
    253 
    254   // Marks the object grey and pushes it on the marking stack.
    255   INLINE(static void MarkObject(Heap* heap, Object* obj)) {
    256     IncrementalMarking::MarkGrey(heap, HeapObject::cast(obj));
    257   }
    258 
    259   // Marks the object black without pushing it on the marking stack.
    260   // Returns true if object needed marking and false otherwise.
    261   INLINE(static bool MarkObjectWithoutPush(Heap* heap, Object* obj)) {
    262     HeapObject* heap_object = HeapObject::cast(obj);
    263     if (ObjectMarking::IsWhite(heap_object)) {
    264       ObjectMarking::WhiteToBlack(heap_object);
    265       return true;
    266     }
    267     return false;
    268   }
    269 };
    270 
    271 void IncrementalMarking::IterateBlackObject(HeapObject* object) {
    272   if (IsMarking() && ObjectMarking::IsBlack(object)) {
    273     Page* page = Page::FromAddress(object->address());
    274     if ((page->owner() != nullptr) && (page->owner()->identity() == LO_SPACE)) {
    275       // IterateBlackObject requires us to visit the whole object.
    276       page->ResetProgressBar();
    277     }
    278     Map* map = object->map();
    279     MarkGrey(heap_, map);
    280     IncrementalMarkingMarkingVisitor::IterateBody(map, object);
    281   }
    282 }
    283 
    284 class IncrementalMarkingRootMarkingVisitor : public ObjectVisitor {
    285  public:
    286   explicit IncrementalMarkingRootMarkingVisitor(
    287       IncrementalMarking* incremental_marking)
    288       : heap_(incremental_marking->heap()) {}
    289 
    290   void VisitPointer(Object** p) override { MarkObjectByPointer(p); }
    291 
    292   void VisitPointers(Object** start, Object** end) override {
    293     for (Object** p = start; p < end; p++) MarkObjectByPointer(p);
    294   }
    295 
    296  private:
    297   void MarkObjectByPointer(Object** p) {
    298     Object* obj = *p;
    299     if (!obj->IsHeapObject()) return;
    300 
    301     IncrementalMarking::MarkGrey(heap_, HeapObject::cast(obj));
    302   }
    303 
    304   Heap* heap_;
    305 };
    306 
    307 
    308 void IncrementalMarking::Initialize() {
    309   IncrementalMarkingMarkingVisitor::Initialize();
    310 }
    311 
    312 
    313 void IncrementalMarking::SetOldSpacePageFlags(MemoryChunk* chunk,
    314                                               bool is_marking,
    315                                               bool is_compacting) {
    316   if (is_marking) {
    317     chunk->SetFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING);
    318     chunk->SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING);
    319   } else {
    320     chunk->ClearFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING);
    321     chunk->SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING);
    322   }
    323 }
    324 
    325 
    326 void IncrementalMarking::SetNewSpacePageFlags(MemoryChunk* chunk,
    327                                               bool is_marking) {
    328   chunk->SetFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING);
    329   if (is_marking) {
    330     chunk->SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING);
    331   } else {
    332     chunk->ClearFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING);
    333   }
    334 }
    335 
    336 
    337 void IncrementalMarking::DeactivateIncrementalWriteBarrierForSpace(
    338     PagedSpace* space) {
    339   for (Page* p : *space) {
    340     SetOldSpacePageFlags(p, false, false);
    341   }
    342 }
    343 
    344 
    345 void IncrementalMarking::DeactivateIncrementalWriteBarrierForSpace(
    346     NewSpace* space) {
    347   for (Page* p : *space) {
    348     SetNewSpacePageFlags(p, false);
    349   }
    350 }
    351 
    352 
    353 void IncrementalMarking::DeactivateIncrementalWriteBarrier() {
    354   DeactivateIncrementalWriteBarrierForSpace(heap_->old_space());
    355   DeactivateIncrementalWriteBarrierForSpace(heap_->map_space());
    356   DeactivateIncrementalWriteBarrierForSpace(heap_->code_space());
    357   DeactivateIncrementalWriteBarrierForSpace(heap_->new_space());
    358 
    359   for (LargePage* lop : *heap_->lo_space()) {
    360     SetOldSpacePageFlags(lop, false, false);
    361   }
    362 }
    363 
    364 
    365 void IncrementalMarking::ActivateIncrementalWriteBarrier(PagedSpace* space) {
    366   for (Page* p : *space) {
    367     SetOldSpacePageFlags(p, true, is_compacting_);
    368   }
    369 }
    370 
    371 
    372 void IncrementalMarking::ActivateIncrementalWriteBarrier(NewSpace* space) {
    373   for (Page* p : *space) {
    374     SetNewSpacePageFlags(p, true);
    375   }
    376 }
    377 
    378 
    379 void IncrementalMarking::ActivateIncrementalWriteBarrier() {
    380   ActivateIncrementalWriteBarrier(heap_->old_space());
    381   ActivateIncrementalWriteBarrier(heap_->map_space());
    382   ActivateIncrementalWriteBarrier(heap_->code_space());
    383   ActivateIncrementalWriteBarrier(heap_->new_space());
    384 
    385   for (LargePage* lop : *heap_->lo_space()) {
    386     SetOldSpacePageFlags(lop, true, is_compacting_);
    387   }
    388 }
    389 
    390 
    391 bool IncrementalMarking::WasActivated() { return was_activated_; }
    392 
    393 
    394 bool IncrementalMarking::CanBeActivated() {
    395   // Only start incremental marking in a safe state: 1) when incremental
    396   // marking is turned on, 2) when we are currently not in a GC, and
    397   // 3) when we are currently not serializing or deserializing the heap.
    398   return FLAG_incremental_marking && heap_->gc_state() == Heap::NOT_IN_GC &&
    399          heap_->deserialization_complete() &&
    400          !heap_->isolate()->serializer_enabled();
    401 }
    402 
    403 
    404 void IncrementalMarking::ActivateGeneratedStub(Code* stub) {
    405   DCHECK(RecordWriteStub::GetMode(stub) == RecordWriteStub::STORE_BUFFER_ONLY);
    406 
    407   if (!IsMarking()) {
    408     // Initially stub is generated in STORE_BUFFER_ONLY mode thus
    409     // we don't need to do anything if incremental marking is
    410     // not active.
    411   } else if (IsCompacting()) {
    412     RecordWriteStub::Patch(stub, RecordWriteStub::INCREMENTAL_COMPACTION);
    413   } else {
    414     RecordWriteStub::Patch(stub, RecordWriteStub::INCREMENTAL);
    415   }
    416 }
    417 
    418 
    419 static void PatchIncrementalMarkingRecordWriteStubs(
    420     Heap* heap, RecordWriteStub::Mode mode) {
    421   UnseededNumberDictionary* stubs = heap->code_stubs();
    422 
    423   int capacity = stubs->Capacity();
    424   Isolate* isolate = heap->isolate();
    425   for (int i = 0; i < capacity; i++) {
    426     Object* k = stubs->KeyAt(i);
    427     if (stubs->IsKey(isolate, k)) {
    428       uint32_t key = NumberToUint32(k);
    429 
    430       if (CodeStub::MajorKeyFromKey(key) == CodeStub::RecordWrite) {
    431         Object* e = stubs->ValueAt(i);
    432         if (e->IsCode()) {
    433           RecordWriteStub::Patch(Code::cast(e), mode);
    434         }
    435       }
    436     }
    437   }
    438 }
    439 
    440 void IncrementalMarking::Start(GarbageCollectionReason gc_reason) {
    441   if (FLAG_trace_incremental_marking) {
    442     int old_generation_size_mb =
    443         static_cast<int>(heap()->PromotedSpaceSizeOfObjects() / MB);
    444     int old_generation_limit_mb =
    445         static_cast<int>(heap()->old_generation_allocation_limit() / MB);
    446     heap()->isolate()->PrintWithTimestamp(
    447         "[IncrementalMarking] Start (%s): old generation %dMB, limit %dMB, "
    448         "slack %dMB\n",
    449         Heap::GarbageCollectionReasonToString(gc_reason),
    450         old_generation_size_mb, old_generation_limit_mb,
    451         Max(0, old_generation_limit_mb - old_generation_size_mb));
    452   }
    453   DCHECK(FLAG_incremental_marking);
    454   DCHECK(state_ == STOPPED);
    455   DCHECK(heap_->gc_state() == Heap::NOT_IN_GC);
    456   DCHECK(!heap_->isolate()->serializer_enabled());
    457 
    458   Counters* counters = heap_->isolate()->counters();
    459 
    460   counters->incremental_marking_reason()->AddSample(
    461       static_cast<int>(gc_reason));
    462   HistogramTimerScope incremental_marking_scope(
    463       counters->gc_incremental_marking_start());
    464   TRACE_EVENT0("v8", "V8.GCIncrementalMarkingStart");
    465   heap_->tracer()->NotifyIncrementalMarkingStart();
    466 
    467   start_time_ms_ = heap()->MonotonicallyIncreasingTimeInMs();
    468   initial_old_generation_size_ = heap_->PromotedSpaceSizeOfObjects();
    469   old_generation_allocation_counter_ = heap_->OldGenerationAllocationCounter();
    470   bytes_allocated_ = 0;
    471   bytes_marked_ahead_of_schedule_ = 0;
    472   should_hurry_ = false;
    473   was_activated_ = true;
    474 
    475   if (!heap_->mark_compact_collector()->sweeping_in_progress()) {
    476     StartMarking();
    477   } else {
    478     if (FLAG_trace_incremental_marking) {
    479       heap()->isolate()->PrintWithTimestamp(
    480           "[IncrementalMarking] Start sweeping.\n");
    481     }
    482     state_ = SWEEPING;
    483   }
    484 
    485   SpaceIterator it(heap_);
    486   while (it.has_next()) {
    487     Space* space = it.next();
    488     if (space == heap_->new_space()) {
    489       space->AddAllocationObserver(&new_generation_observer_);
    490     } else {
    491       space->AddAllocationObserver(&old_generation_observer_);
    492     }
    493   }
    494 
    495   incremental_marking_job()->Start(heap_);
    496 }
    497 
    498 
    499 void IncrementalMarking::StartMarking() {
    500   if (heap_->isolate()->serializer_enabled()) {
    501     // Black allocation currently starts when we start incremental marking,
    502     // but we cannot enable black allocation while deserializing. Hence, we
    503     // have to delay the start of incremental marking in that case.
    504     if (FLAG_trace_incremental_marking) {
    505       heap()->isolate()->PrintWithTimestamp(
    506           "[IncrementalMarking] Start delayed - serializer\n");
    507     }
    508     return;
    509   }
    510   if (FLAG_trace_incremental_marking) {
    511     heap()->isolate()->PrintWithTimestamp(
    512         "[IncrementalMarking] Start marking\n");
    513   }
    514 
    515   is_compacting_ =
    516       !FLAG_never_compact && heap_->mark_compact_collector()->StartCompaction();
    517 
    518   state_ = MARKING;
    519 
    520   {
    521     TRACE_GC(heap()->tracer(),
    522              GCTracer::Scope::MC_INCREMENTAL_WRAPPER_PROLOGUE);
    523     heap_->local_embedder_heap_tracer()->TracePrologue();
    524   }
    525 
    526   RecordWriteStub::Mode mode = is_compacting_
    527                                    ? RecordWriteStub::INCREMENTAL_COMPACTION
    528                                    : RecordWriteStub::INCREMENTAL;
    529 
    530   PatchIncrementalMarkingRecordWriteStubs(heap_, mode);
    531 
    532   heap_->mark_compact_collector()->marking_deque()->StartUsing();
    533 
    534   ActivateIncrementalWriteBarrier();
    535 
    536 // Marking bits are cleared by the sweeper.
    537 #ifdef VERIFY_HEAP
    538   if (FLAG_verify_heap) {
    539     heap_->mark_compact_collector()->VerifyMarkbitsAreClean();
    540   }
    541 #endif
    542 
    543   heap_->CompletelyClearInstanceofCache();
    544   heap_->isolate()->compilation_cache()->MarkCompactPrologue();
    545 
    546   // Mark strong roots grey.
    547   IncrementalMarkingRootMarkingVisitor visitor(this);
    548   heap_->IterateStrongRoots(&visitor, VISIT_ONLY_STRONG);
    549 
    550   // Ready to start incremental marking.
    551   if (FLAG_trace_incremental_marking) {
    552     heap()->isolate()->PrintWithTimestamp("[IncrementalMarking] Running\n");
    553   }
    554 }
    555 
    556 void IncrementalMarking::StartBlackAllocation() {
    557   DCHECK(FLAG_black_allocation);
    558   DCHECK(IsMarking());
    559   black_allocation_ = true;
    560   heap()->old_space()->MarkAllocationInfoBlack();
    561   heap()->map_space()->MarkAllocationInfoBlack();
    562   heap()->code_space()->MarkAllocationInfoBlack();
    563   if (FLAG_trace_incremental_marking) {
    564     heap()->isolate()->PrintWithTimestamp(
    565         "[IncrementalMarking] Black allocation started\n");
    566   }
    567 }
    568 
    569 void IncrementalMarking::FinishBlackAllocation() {
    570   if (black_allocation_) {
    571     black_allocation_ = false;
    572     if (FLAG_trace_incremental_marking) {
    573       heap()->isolate()->PrintWithTimestamp(
    574           "[IncrementalMarking] Black allocation finished\n");
    575     }
    576   }
    577 }
    578 
    579 void IncrementalMarking::AbortBlackAllocation() {
    580   if (FLAG_trace_incremental_marking) {
    581     heap()->isolate()->PrintWithTimestamp(
    582         "[IncrementalMarking] Black allocation aborted\n");
    583   }
    584 }
    585 
    586 void IncrementalMarking::MarkRoots() {
    587   DCHECK(!finalize_marking_completed_);
    588   DCHECK(IsMarking());
    589 
    590   IncrementalMarkingRootMarkingVisitor visitor(this);
    591   heap_->IterateStrongRoots(&visitor, VISIT_ONLY_STRONG);
    592 }
    593 
    594 
    595 void IncrementalMarking::MarkObjectGroups() {
    596   TRACE_GC(heap_->tracer(),
    597            GCTracer::Scope::MC_INCREMENTAL_FINALIZE_OBJECT_GROUPING);
    598 
    599   DCHECK(!heap_->local_embedder_heap_tracer()->InUse());
    600   DCHECK(!finalize_marking_completed_);
    601   DCHECK(IsMarking());
    602 
    603   IncrementalMarkingRootMarkingVisitor visitor(this);
    604   heap_->mark_compact_collector()->MarkImplicitRefGroups(&MarkGrey);
    605   heap_->isolate()->global_handles()->IterateObjectGroups(
    606       &visitor, &MarkCompactCollector::IsUnmarkedHeapObjectWithHeap);
    607   heap_->isolate()->global_handles()->RemoveImplicitRefGroups();
    608   heap_->isolate()->global_handles()->RemoveObjectGroups();
    609 }
    610 
    611 
    612 void IncrementalMarking::ProcessWeakCells() {
    613   DCHECK(!finalize_marking_completed_);
    614   DCHECK(IsMarking());
    615 
    616   Object* the_hole_value = heap()->the_hole_value();
    617   Object* weak_cell_obj = heap()->encountered_weak_cells();
    618   Object* weak_cell_head = Smi::kZero;
    619   WeakCell* prev_weak_cell_obj = NULL;
    620   while (weak_cell_obj != Smi::kZero) {
    621     WeakCell* weak_cell = reinterpret_cast<WeakCell*>(weak_cell_obj);
    622     // We do not insert cleared weak cells into the list, so the value
    623     // cannot be a Smi here.
    624     HeapObject* value = HeapObject::cast(weak_cell->value());
    625     // Remove weak cells with live objects from the list, they do not need
    626     // clearing.
    627     if (ObjectMarking::IsBlackOrGrey(value)) {
    628       // Record slot, if value is pointing to an evacuation candidate.
    629       Object** slot = HeapObject::RawField(weak_cell, WeakCell::kValueOffset);
    630       heap_->mark_compact_collector()->RecordSlot(weak_cell, slot, *slot);
    631       // Remove entry somewhere after top.
    632       if (prev_weak_cell_obj != NULL) {
    633         prev_weak_cell_obj->set_next(weak_cell->next());
    634       }
    635       weak_cell_obj = weak_cell->next();
    636       weak_cell->clear_next(the_hole_value);
    637     } else {
    638       if (weak_cell_head == Smi::kZero) {
    639         weak_cell_head = weak_cell;
    640       }
    641       prev_weak_cell_obj = weak_cell;
    642       weak_cell_obj = weak_cell->next();
    643     }
    644   }
    645   // Top may have changed.
    646   heap()->set_encountered_weak_cells(weak_cell_head);
    647 }
    648 
    649 
    650 bool ShouldRetainMap(Map* map, int age) {
    651   if (age == 0) {
    652     // The map has aged. Do not retain this map.
    653     return false;
    654   }
    655   Object* constructor = map->GetConstructor();
    656   if (!constructor->IsHeapObject() ||
    657       ObjectMarking::IsWhite(HeapObject::cast(constructor))) {
    658     // The constructor is dead, no new objects with this map can
    659     // be created. Do not retain this map.
    660     return false;
    661   }
    662   return true;
    663 }
    664 
    665 
    666 void IncrementalMarking::RetainMaps() {
    667   // Do not retain dead maps if flag disables it or there is
    668   // - memory pressure (reduce_memory_footprint_),
    669   // - GC is requested by tests or dev-tools (abort_incremental_marking_).
    670   bool map_retaining_is_disabled = heap()->ShouldReduceMemory() ||
    671                                    heap()->ShouldAbortIncrementalMarking() ||
    672                                    FLAG_retain_maps_for_n_gc == 0;
    673   ArrayList* retained_maps = heap()->retained_maps();
    674   int length = retained_maps->Length();
    675   // The number_of_disposed_maps separates maps in the retained_maps
    676   // array that were created before and after context disposal.
    677   // We do not age and retain disposed maps to avoid memory leaks.
    678   int number_of_disposed_maps = heap()->number_of_disposed_maps_;
    679   for (int i = 0; i < length; i += 2) {
    680     DCHECK(retained_maps->Get(i)->IsWeakCell());
    681     WeakCell* cell = WeakCell::cast(retained_maps->Get(i));
    682     if (cell->cleared()) continue;
    683     int age = Smi::cast(retained_maps->Get(i + 1))->value();
    684     int new_age;
    685     Map* map = Map::cast(cell->value());
    686     if (i >= number_of_disposed_maps && !map_retaining_is_disabled &&
    687         ObjectMarking::IsWhite(map)) {
    688       if (ShouldRetainMap(map, age)) {
    689         MarkGrey(heap(), map);
    690       }
    691       Object* prototype = map->prototype();
    692       if (age > 0 && prototype->IsHeapObject() &&
    693           ObjectMarking::IsWhite(HeapObject::cast(prototype))) {
    694         // The prototype is not marked, age the map.
    695         new_age = age - 1;
    696       } else {
    697         // The prototype and the constructor are marked, this map keeps only
    698         // transition tree alive, not JSObjects. Do not age the map.
    699         new_age = age;
    700       }
    701     } else {
    702       new_age = FLAG_retain_maps_for_n_gc;
    703     }
    704     // Compact the array and update the age.
    705     if (new_age != age) {
    706       retained_maps->Set(i + 1, Smi::FromInt(new_age));
    707     }
    708   }
    709 }
    710 
    711 void IncrementalMarking::FinalizeIncrementally() {
    712   TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_INCREMENTAL_FINALIZE_BODY);
    713   DCHECK(!finalize_marking_completed_);
    714   DCHECK(IsMarking());
    715 
    716   double start = heap_->MonotonicallyIncreasingTimeInMs();
    717 
    718   int old_marking_deque_top =
    719       heap_->mark_compact_collector()->marking_deque()->top();
    720 
    721   // After finishing incremental marking, we try to discover all unmarked
    722   // objects to reduce the marking load in the final pause.
    723   // 1) We scan and mark the roots again to find all changes to the root set.
    724   // 2) We mark the object groups.
    725   // 3) Age and retain maps embedded in optimized code.
    726   // 4) Remove weak cell with live values from the list of weak cells, they
    727   // do not need processing during GC.
    728   MarkRoots();
    729   if (!heap_->local_embedder_heap_tracer()->InUse()) {
    730     MarkObjectGroups();
    731   }
    732   if (incremental_marking_finalization_rounds_ == 0) {
    733     // Map retaining is needed for perfromance, not correctness,
    734     // so we can do it only once at the beginning of the finalization.
    735     RetainMaps();
    736   }
    737   ProcessWeakCells();
    738 
    739   int marking_progress =
    740       abs(old_marking_deque_top -
    741           heap_->mark_compact_collector()->marking_deque()->top());
    742 
    743   marking_progress += static_cast<int>(
    744       heap_->local_embedder_heap_tracer()->NumberOfCachedWrappersToTrace());
    745 
    746   double end = heap_->MonotonicallyIncreasingTimeInMs();
    747   double delta = end - start;
    748   if (FLAG_trace_incremental_marking) {
    749     heap()->isolate()->PrintWithTimestamp(
    750         "[IncrementalMarking] Finalize incrementally round %d, "
    751         "spent %d ms, marking progress %d.\n",
    752         static_cast<int>(delta), incremental_marking_finalization_rounds_,
    753         marking_progress);
    754   }
    755 
    756   ++incremental_marking_finalization_rounds_;
    757   if ((incremental_marking_finalization_rounds_ >=
    758        FLAG_max_incremental_marking_finalization_rounds) ||
    759       (marking_progress <
    760        FLAG_min_progress_during_incremental_marking_finalization)) {
    761     finalize_marking_completed_ = true;
    762   }
    763 
    764   if (FLAG_black_allocation && !heap()->ShouldReduceMemory() &&
    765       !black_allocation_) {
    766     // TODO(hpayer): Move to an earlier point as soon as we make faster marking
    767     // progress.
    768     StartBlackAllocation();
    769   }
    770 }
    771 
    772 
    773 void IncrementalMarking::UpdateMarkingDequeAfterScavenge() {
    774   if (!IsMarking()) return;
    775 
    776   MarkingDeque* marking_deque =
    777       heap_->mark_compact_collector()->marking_deque();
    778   int current = marking_deque->bottom();
    779   int mask = marking_deque->mask();
    780   int limit = marking_deque->top();
    781   HeapObject** array = marking_deque->array();
    782   int new_top = current;
    783 
    784   Map* filler_map = heap_->one_pointer_filler_map();
    785 
    786   while (current != limit) {
    787     HeapObject* obj = array[current];
    788     DCHECK(obj->IsHeapObject());
    789     current = ((current + 1) & mask);
    790     // Only pointers to from space have to be updated.
    791     if (heap_->InFromSpace(obj)) {
    792       MapWord map_word = obj->map_word();
    793       // There may be objects on the marking deque that do not exist anymore,
    794       // e.g. left trimmed objects or objects from the root set (frames).
    795       // If these object are dead at scavenging time, their marking deque
    796       // entries will not point to forwarding addresses. Hence, we can discard
    797       // them.
    798       if (map_word.IsForwardingAddress()) {
    799         HeapObject* dest = map_word.ToForwardingAddress();
    800         if (ObjectMarking::IsBlack(dest)) continue;
    801         array[new_top] = dest;
    802         new_top = ((new_top + 1) & mask);
    803         DCHECK(new_top != marking_deque->bottom());
    804         DCHECK(ObjectMarking::IsGrey(obj) ||
    805                (obj->IsFiller() && ObjectMarking::IsWhite(obj)));
    806       }
    807     } else if (obj->map() != filler_map) {
    808       // Skip one word filler objects that appear on the
    809       // stack when we perform in place array shift.
    810       array[new_top] = obj;
    811       new_top = ((new_top + 1) & mask);
    812       DCHECK(new_top != marking_deque->bottom());
    813       DCHECK(ObjectMarking::IsGrey(obj) ||
    814              (obj->IsFiller() && ObjectMarking::IsWhite(obj)) ||
    815              (MemoryChunk::FromAddress(obj->address())
    816                   ->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR) &&
    817               ObjectMarking::IsBlack(obj)));
    818     }
    819   }
    820   marking_deque->set_top(new_top);
    821 }
    822 
    823 
    824 void IncrementalMarking::VisitObject(Map* map, HeapObject* obj, int size) {
    825   MarkGrey(heap_, map);
    826 
    827   IncrementalMarkingMarkingVisitor::IterateBody(map, obj);
    828 
    829 #if ENABLE_SLOW_DCHECKS
    830   MarkBit mark_bit = ObjectMarking::MarkBitFrom(obj);
    831   MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
    832   SLOW_DCHECK(Marking::IsGrey(mark_bit) ||
    833               (obj->IsFiller() && Marking::IsWhite(mark_bit)) ||
    834               (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR) &&
    835                Marking::IsBlack(mark_bit)));
    836 #endif
    837   MarkBlack(obj, size);
    838 }
    839 
    840 void IncrementalMarking::MarkGrey(Heap* heap, HeapObject* object) {
    841   if (ObjectMarking::IsWhite(object)) {
    842     heap->incremental_marking()->WhiteToGreyAndPush(object);
    843   }
    844 }
    845 
    846 void IncrementalMarking::MarkBlack(HeapObject* obj, int size) {
    847   if (ObjectMarking::IsBlack(obj)) return;
    848   ObjectMarking::GreyToBlack(obj);
    849 }
    850 
    851 intptr_t IncrementalMarking::ProcessMarkingDeque(
    852     intptr_t bytes_to_process, ForceCompletionAction completion) {
    853   intptr_t bytes_processed = 0;
    854   MarkingDeque* marking_deque =
    855       heap_->mark_compact_collector()->marking_deque();
    856   while (!marking_deque->IsEmpty() && (bytes_processed < bytes_to_process ||
    857                                        completion == FORCE_COMPLETION)) {
    858     HeapObject* obj = marking_deque->Pop();
    859 
    860     // Left trimming may result in white filler objects on the marking deque.
    861     // Ignore these objects.
    862     if (obj->IsFiller()) {
    863       DCHECK(ObjectMarking::IsImpossible(obj) || ObjectMarking::IsWhite(obj));
    864       continue;
    865     }
    866 
    867     Map* map = obj->map();
    868     int size = obj->SizeFromMap(map);
    869     unscanned_bytes_of_large_object_ = 0;
    870     VisitObject(map, obj, size);
    871     bytes_processed += size - unscanned_bytes_of_large_object_;
    872   }
    873   // Report all found wrappers to the embedder. This is necessary as the
    874   // embedder could potentially invalidate wrappers as soon as V8 is done
    875   // with its incremental marking processing. Any cached wrappers could
    876   // result in broken pointers at this point.
    877   heap_->local_embedder_heap_tracer()->RegisterWrappersWithRemoteTracer();
    878   return bytes_processed;
    879 }
    880 
    881 
    882 void IncrementalMarking::Hurry() {
    883   // A scavenge may have pushed new objects on the marking deque (due to black
    884   // allocation) even in COMPLETE state. This may happen if scavenges are
    885   // forced e.g. in tests. It should not happen when COMPLETE was set when
    886   // incremental marking finished and a regular GC was triggered after that
    887   // because should_hurry_ will force a full GC.
    888   if (!heap_->mark_compact_collector()->marking_deque()->IsEmpty()) {
    889     double start = 0.0;
    890     if (FLAG_trace_incremental_marking) {
    891       start = heap_->MonotonicallyIncreasingTimeInMs();
    892       if (FLAG_trace_incremental_marking) {
    893         heap()->isolate()->PrintWithTimestamp("[IncrementalMarking] Hurry\n");
    894       }
    895     }
    896     // TODO(gc) hurry can mark objects it encounters black as mutator
    897     // was stopped.
    898     ProcessMarkingDeque(0, FORCE_COMPLETION);
    899     state_ = COMPLETE;
    900     if (FLAG_trace_incremental_marking) {
    901       double end = heap_->MonotonicallyIncreasingTimeInMs();
    902       double delta = end - start;
    903       if (FLAG_trace_incremental_marking) {
    904         heap()->isolate()->PrintWithTimestamp(
    905             "[IncrementalMarking] Complete (hurry), spent %d ms.\n",
    906             static_cast<int>(delta));
    907       }
    908     }
    909   }
    910 
    911   Object* context = heap_->native_contexts_list();
    912   while (!context->IsUndefined(heap_->isolate())) {
    913     // GC can happen when the context is not fully initialized,
    914     // so the cache can be undefined.
    915     HeapObject* cache = HeapObject::cast(
    916         Context::cast(context)->get(Context::NORMALIZED_MAP_CACHE_INDEX));
    917     if (!cache->IsUndefined(heap_->isolate())) {
    918       if (ObjectMarking::IsGrey(cache)) {
    919         ObjectMarking::GreyToBlack(cache);
    920       }
    921     }
    922     context = Context::cast(context)->next_context_link();
    923   }
    924 }
    925 
    926 
    927 void IncrementalMarking::Stop() {
    928   if (IsStopped()) return;
    929   if (FLAG_trace_incremental_marking) {
    930     int old_generation_size_mb =
    931         static_cast<int>(heap()->PromotedSpaceSizeOfObjects() / MB);
    932     int old_generation_limit_mb =
    933         static_cast<int>(heap()->old_generation_allocation_limit() / MB);
    934     heap()->isolate()->PrintWithTimestamp(
    935         "[IncrementalMarking] Stopping: old generation %dMB, limit %dMB, "
    936         "overshoot %dMB\n",
    937         old_generation_size_mb, old_generation_limit_mb,
    938         Max(0, old_generation_size_mb - old_generation_limit_mb));
    939   }
    940 
    941   SpaceIterator it(heap_);
    942   while (it.has_next()) {
    943     Space* space = it.next();
    944     if (space == heap_->new_space()) {
    945       space->RemoveAllocationObserver(&new_generation_observer_);
    946     } else {
    947       space->RemoveAllocationObserver(&old_generation_observer_);
    948     }
    949   }
    950 
    951   IncrementalMarking::set_should_hurry(false);
    952   if (IsMarking()) {
    953     PatchIncrementalMarkingRecordWriteStubs(heap_,
    954                                             RecordWriteStub::STORE_BUFFER_ONLY);
    955     DeactivateIncrementalWriteBarrier();
    956   }
    957   heap_->isolate()->stack_guard()->ClearGC();
    958   state_ = STOPPED;
    959   is_compacting_ = false;
    960   FinishBlackAllocation();
    961 }
    962 
    963 
    964 void IncrementalMarking::Finalize() {
    965   Hurry();
    966   Stop();
    967 }
    968 
    969 
    970 void IncrementalMarking::FinalizeMarking(CompletionAction action) {
    971   DCHECK(!finalize_marking_completed_);
    972   if (FLAG_trace_incremental_marking) {
    973     heap()->isolate()->PrintWithTimestamp(
    974         "[IncrementalMarking] requesting finalization of incremental "
    975         "marking.\n");
    976   }
    977   request_type_ = FINALIZATION;
    978   if (action == GC_VIA_STACK_GUARD) {
    979     heap_->isolate()->stack_guard()->RequestGC();
    980   }
    981 }
    982 
    983 
    984 void IncrementalMarking::MarkingComplete(CompletionAction action) {
    985   state_ = COMPLETE;
    986   // We will set the stack guard to request a GC now.  This will mean the rest
    987   // of the GC gets performed as soon as possible (we can't do a GC here in a
    988   // record-write context).  If a few things get allocated between now and then
    989   // that shouldn't make us do a scavenge and keep being incremental, so we set
    990   // the should-hurry flag to indicate that there can't be much work left to do.
    991   set_should_hurry(true);
    992   if (FLAG_trace_incremental_marking) {
    993     heap()->isolate()->PrintWithTimestamp(
    994         "[IncrementalMarking] Complete (normal).\n");
    995   }
    996   request_type_ = COMPLETE_MARKING;
    997   if (action == GC_VIA_STACK_GUARD) {
    998     heap_->isolate()->stack_guard()->RequestGC();
    999   }
   1000 }
   1001 
   1002 
   1003 void IncrementalMarking::Epilogue() {
   1004   was_activated_ = false;
   1005   finalize_marking_completed_ = false;
   1006   incremental_marking_finalization_rounds_ = 0;
   1007 }
   1008 
   1009 double IncrementalMarking::AdvanceIncrementalMarking(
   1010     double deadline_in_ms, CompletionAction completion_action,
   1011     ForceCompletionAction force_completion, StepOrigin step_origin) {
   1012   HistogramTimerScope incremental_marking_scope(
   1013       heap_->isolate()->counters()->gc_incremental_marking());
   1014   TRACE_EVENT0("v8", "V8.GCIncrementalMarking");
   1015   TRACE_GC(heap_->tracer(), GCTracer::Scope::MC_INCREMENTAL);
   1016   DCHECK(!IsStopped());
   1017   DCHECK_EQ(
   1018       0, heap_->local_embedder_heap_tracer()->NumberOfCachedWrappersToTrace());
   1019 
   1020   double remaining_time_in_ms = 0.0;
   1021   intptr_t step_size_in_bytes = GCIdleTimeHandler::EstimateMarkingStepSize(
   1022       kStepSizeInMs,
   1023       heap()->tracer()->IncrementalMarkingSpeedInBytesPerMillisecond());
   1024 
   1025   const bool incremental_wrapper_tracing =
   1026       state_ == MARKING && FLAG_incremental_marking_wrappers &&
   1027       heap_->local_embedder_heap_tracer()->InUse();
   1028   do {
   1029     if (incremental_wrapper_tracing && trace_wrappers_toggle_) {
   1030       TRACE_GC(heap()->tracer(),
   1031                GCTracer::Scope::MC_INCREMENTAL_WRAPPER_TRACING);
   1032       const double wrapper_deadline =
   1033           heap_->MonotonicallyIncreasingTimeInMs() + kStepSizeInMs;
   1034       if (!heap_->local_embedder_heap_tracer()
   1035                ->ShouldFinalizeIncrementalMarking()) {
   1036         heap_->local_embedder_heap_tracer()->Trace(
   1037             wrapper_deadline, EmbedderHeapTracer::AdvanceTracingActions(
   1038                                   EmbedderHeapTracer::ForceCompletionAction::
   1039                                       DO_NOT_FORCE_COMPLETION));
   1040       }
   1041     } else {
   1042       Step(step_size_in_bytes, completion_action, force_completion,
   1043            step_origin);
   1044     }
   1045     trace_wrappers_toggle_ = !trace_wrappers_toggle_;
   1046     remaining_time_in_ms =
   1047         deadline_in_ms - heap()->MonotonicallyIncreasingTimeInMs();
   1048   } while (remaining_time_in_ms >= kStepSizeInMs && !IsComplete() &&
   1049            !heap()->mark_compact_collector()->marking_deque()->IsEmpty());
   1050   return remaining_time_in_ms;
   1051 }
   1052 
   1053 
   1054 void IncrementalMarking::FinalizeSweeping() {
   1055   DCHECK(state_ == SWEEPING);
   1056   if (heap_->mark_compact_collector()->sweeping_in_progress() &&
   1057       (!FLAG_concurrent_sweeping ||
   1058        !heap_->mark_compact_collector()->sweeper().AreSweeperTasksRunning())) {
   1059     heap_->mark_compact_collector()->EnsureSweepingCompleted();
   1060   }
   1061   if (!heap_->mark_compact_collector()->sweeping_in_progress()) {
   1062     StartMarking();
   1063   }
   1064 }
   1065 
   1066 size_t IncrementalMarking::StepSizeToKeepUpWithAllocations() {
   1067   // Update bytes_allocated_ based on the allocation counter.
   1068   size_t current_counter = heap_->OldGenerationAllocationCounter();
   1069   bytes_allocated_ += current_counter - old_generation_allocation_counter_;
   1070   old_generation_allocation_counter_ = current_counter;
   1071   return bytes_allocated_;
   1072 }
   1073 
   1074 size_t IncrementalMarking::StepSizeToMakeProgress() {
   1075   // We increase step size gradually based on the time passed in order to
   1076   // leave marking work to standalone tasks. The ramp up duration and the
   1077   // target step count are chosen based on benchmarks.
   1078   const int kRampUpIntervalMs = 300;
   1079   const size_t kTargetStepCount = 128;
   1080   const size_t kTargetStepCountAtOOM = 16;
   1081   size_t oom_slack = heap()->new_space()->Capacity() + 64 * MB;
   1082 
   1083   if (heap()->IsCloseToOutOfMemory(oom_slack)) {
   1084     return heap()->PromotedSpaceSizeOfObjects() / kTargetStepCountAtOOM;
   1085   }
   1086 
   1087   size_t step_size = Max(initial_old_generation_size_ / kTargetStepCount,
   1088                          IncrementalMarking::kAllocatedThreshold);
   1089   double time_passed_ms =
   1090       heap_->MonotonicallyIncreasingTimeInMs() - start_time_ms_;
   1091   double factor = Min(time_passed_ms / kRampUpIntervalMs, 1.0);
   1092   return static_cast<size_t>(factor * step_size);
   1093 }
   1094 
   1095 void IncrementalMarking::AdvanceIncrementalMarkingOnAllocation() {
   1096   if (heap_->gc_state() != Heap::NOT_IN_GC || !FLAG_incremental_marking ||
   1097       (state_ != SWEEPING && state_ != MARKING)) {
   1098     return;
   1099   }
   1100 
   1101   size_t bytes_to_process =
   1102       StepSizeToKeepUpWithAllocations() + StepSizeToMakeProgress();
   1103 
   1104   if (bytes_to_process >= IncrementalMarking::kAllocatedThreshold) {
   1105     // The first step after Scavenge will see many allocated bytes.
   1106     // Cap the step size to distribute the marking work more uniformly.
   1107     size_t max_step_size = GCIdleTimeHandler::EstimateMarkingStepSize(
   1108         kMaxStepSizeInMs,
   1109         heap()->tracer()->IncrementalMarkingSpeedInBytesPerMillisecond());
   1110     bytes_to_process = Min(bytes_to_process, max_step_size);
   1111 
   1112     size_t bytes_processed = 0;
   1113     if (bytes_marked_ahead_of_schedule_ >= bytes_to_process) {
   1114       // Steps performed in tasks have put us ahead of schedule.
   1115       // We skip processing of marking dequeue here and thus
   1116       // shift marking time from inside V8 to standalone tasks.
   1117       bytes_marked_ahead_of_schedule_ -= bytes_to_process;
   1118       bytes_processed = bytes_to_process;
   1119     } else {
   1120       HistogramTimerScope incremental_marking_scope(
   1121           heap_->isolate()->counters()->gc_incremental_marking());
   1122       TRACE_EVENT0("v8", "V8.GCIncrementalMarking");
   1123       TRACE_GC(heap_->tracer(), GCTracer::Scope::MC_INCREMENTAL);
   1124       bytes_processed = Step(bytes_to_process, GC_VIA_STACK_GUARD,
   1125                              FORCE_COMPLETION, StepOrigin::kV8);
   1126     }
   1127     bytes_allocated_ -= Min(bytes_allocated_, bytes_processed);
   1128   }
   1129 }
   1130 
   1131 size_t IncrementalMarking::Step(size_t bytes_to_process,
   1132                                 CompletionAction action,
   1133                                 ForceCompletionAction completion,
   1134                                 StepOrigin step_origin) {
   1135   double start = heap_->MonotonicallyIncreasingTimeInMs();
   1136 
   1137   if (state_ == SWEEPING) {
   1138     TRACE_GC(heap_->tracer(), GCTracer::Scope::MC_INCREMENTAL_SWEEPING);
   1139     FinalizeSweeping();
   1140   }
   1141 
   1142   size_t bytes_processed = 0;
   1143   if (state_ == MARKING) {
   1144     bytes_processed = ProcessMarkingDeque(bytes_to_process);
   1145     if (step_origin == StepOrigin::kTask) {
   1146       bytes_marked_ahead_of_schedule_ += bytes_processed;
   1147     }
   1148 
   1149     if (heap_->mark_compact_collector()->marking_deque()->IsEmpty()) {
   1150       if (heap_->local_embedder_heap_tracer()
   1151               ->ShouldFinalizeIncrementalMarking()) {
   1152         if (completion == FORCE_COMPLETION ||
   1153             IsIdleMarkingDelayCounterLimitReached()) {
   1154           if (!finalize_marking_completed_) {
   1155             FinalizeMarking(action);
   1156           } else {
   1157             MarkingComplete(action);
   1158           }
   1159         } else {
   1160           IncrementIdleMarkingDelayCounter();
   1161         }
   1162       } else {
   1163         heap_->local_embedder_heap_tracer()->NotifyV8MarkingDequeWasEmpty();
   1164       }
   1165     }
   1166   }
   1167 
   1168   double end = heap_->MonotonicallyIncreasingTimeInMs();
   1169   double duration = (end - start);
   1170   // Note that we report zero bytes here when sweeping was in progress or
   1171   // when we just started incremental marking. In these cases we did not
   1172   // process the marking deque.
   1173   heap_->tracer()->AddIncrementalMarkingStep(duration, bytes_processed);
   1174   if (FLAG_trace_incremental_marking) {
   1175     heap_->isolate()->PrintWithTimestamp(
   1176         "[IncrementalMarking] Step %s %zu bytes (%zu) in %.1f\n",
   1177         step_origin == StepOrigin::kV8 ? "in v8" : "in task", bytes_processed,
   1178         bytes_to_process, duration);
   1179   }
   1180   return bytes_processed;
   1181 }
   1182 
   1183 
   1184 bool IncrementalMarking::IsIdleMarkingDelayCounterLimitReached() {
   1185   return idle_marking_delay_counter_ > kMaxIdleMarkingDelayCounter;
   1186 }
   1187 
   1188 
   1189 void IncrementalMarking::IncrementIdleMarkingDelayCounter() {
   1190   idle_marking_delay_counter_++;
   1191 }
   1192 
   1193 
   1194 void IncrementalMarking::ClearIdleMarkingDelayCounter() {
   1195   idle_marking_delay_counter_ = 0;
   1196 }
   1197 
   1198 }  // namespace internal
   1199 }  // namespace v8
   1200