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