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/concurrent-marking.h"
     11 #include "src/heap/gc-idle-time-handler.h"
     12 #include "src/heap/gc-tracer.h"
     13 #include "src/heap/heap-inl.h"
     14 #include "src/heap/incremental-marking-inl.h"
     15 #include "src/heap/mark-compact-inl.h"
     16 #include "src/heap/object-stats.h"
     17 #include "src/heap/objects-visiting-inl.h"
     18 #include "src/heap/objects-visiting.h"
     19 #include "src/heap/sweeper.h"
     20 #include "src/objects/hash-table-inl.h"
     21 #include "src/tracing/trace-event.h"
     22 #include "src/v8.h"
     23 #include "src/visitors.h"
     24 #include "src/vm-state-inl.h"
     25 
     26 namespace v8 {
     27 namespace internal {
     28 
     29 using IncrementalMarkingMarkingVisitor =
     30     MarkingVisitor<FixedArrayVisitationMode::kIncremental,
     31                    TraceRetainingPathMode::kDisabled,
     32                    IncrementalMarking::MarkingState>;
     33 
     34 void IncrementalMarking::Observer::Step(int bytes_allocated, Address addr,
     35                                         size_t size) {
     36   Heap* heap = incremental_marking_.heap();
     37   VMState<GC> state(heap->isolate());
     38   RuntimeCallTimerScope runtime_timer(
     39       heap->isolate(),
     40       RuntimeCallCounterId::kGC_Custom_IncrementalMarkingObserver);
     41   incremental_marking_.AdvanceIncrementalMarkingOnAllocation();
     42   if (incremental_marking_.black_allocation() && addr != kNullAddress) {
     43     // AdvanceIncrementalMarkingOnAllocation can start black allocation.
     44     // Ensure that the new object is marked black.
     45     HeapObject* object = HeapObject::FromAddress(addr);
     46     if (incremental_marking_.marking_state()->IsWhite(object) &&
     47         !(Heap::InNewSpace(object) || heap->new_lo_space()->Contains(object))) {
     48       if (heap->lo_space()->Contains(object)) {
     49         incremental_marking_.marking_state()->WhiteToBlack(object);
     50       } else {
     51         Page::FromAddress(addr)->CreateBlackArea(addr, addr + size);
     52       }
     53     }
     54   }
     55 }
     56 
     57 IncrementalMarking::IncrementalMarking(
     58     Heap* heap, MarkCompactCollector::MarkingWorklist* marking_worklist,
     59     WeakObjects* weak_objects)
     60     : heap_(heap),
     61       marking_worklist_(marking_worklist),
     62       weak_objects_(weak_objects),
     63       initial_old_generation_size_(0),
     64       bytes_marked_ahead_of_schedule_(0),
     65       bytes_marked_concurrently_(0),
     66       unscanned_bytes_of_large_object_(0),
     67       is_compacting_(false),
     68       should_hurry_(false),
     69       was_activated_(false),
     70       black_allocation_(false),
     71       finalize_marking_completed_(false),
     72       trace_wrappers_toggle_(false),
     73       request_type_(NONE),
     74       new_generation_observer_(*this, kYoungGenerationAllocatedThreshold),
     75       old_generation_observer_(*this, kOldGenerationAllocatedThreshold) {
     76   DCHECK_NOT_NULL(marking_worklist_);
     77   SetState(STOPPED);
     78 }
     79 
     80 bool IncrementalMarking::BaseRecordWrite(HeapObject* obj, Object* value) {
     81   HeapObject* value_heap_obj = HeapObject::cast(value);
     82   DCHECK(!marking_state()->IsImpossible(value_heap_obj));
     83   DCHECK(!marking_state()->IsImpossible(obj));
     84 #ifdef V8_CONCURRENT_MARKING
     85   // The write barrier stub generated with V8_CONCURRENT_MARKING does not
     86   // check the color of the source object.
     87   const bool need_recording = true;
     88 #else
     89   const bool need_recording = marking_state()->IsBlack(obj);
     90 #endif
     91 
     92   if (need_recording && WhiteToGreyAndPush(value_heap_obj)) {
     93     RestartIfNotMarking();
     94   }
     95   return is_compacting_ && need_recording;
     96 }
     97 
     98 void IncrementalMarking::RecordWriteSlow(HeapObject* obj,
     99                                          HeapObjectReference** slot,
    100                                          Object* value) {
    101   if (BaseRecordWrite(obj, value) && slot != nullptr) {
    102     // Object is not going to be rescanned we need to record the slot.
    103     heap_->mark_compact_collector()->RecordSlot(obj, slot,
    104                                                 HeapObject::cast(value));
    105   }
    106 }
    107 
    108 int IncrementalMarking::RecordWriteFromCode(HeapObject* obj, MaybeObject** slot,
    109                                             Isolate* isolate) {
    110   DCHECK(obj->IsHeapObject());
    111   isolate->heap()->incremental_marking()->RecordMaybeWeakWrite(obj, slot,
    112                                                                *slot);
    113   // Called by RecordWriteCodeStubAssembler, which doesnt accept void type
    114   return 0;
    115 }
    116 
    117 void IncrementalMarking::RecordWriteIntoCode(Code* host, RelocInfo* rinfo,
    118                                              HeapObject* value) {
    119   DCHECK(IsMarking());
    120   if (BaseRecordWrite(host, value)) {
    121     // Object is not going to be rescanned.  We need to record the slot.
    122     heap_->mark_compact_collector()->RecordRelocSlot(host, rinfo, value);
    123   }
    124 }
    125 
    126 bool IncrementalMarking::WhiteToGreyAndPush(HeapObject* obj) {
    127   if (marking_state()->WhiteToGrey(obj)) {
    128     marking_worklist()->Push(obj);
    129     return true;
    130   }
    131   return false;
    132 }
    133 
    134 void IncrementalMarking::MarkBlackAndPush(HeapObject* obj) {
    135   // Marking left-trimmable fixed array black is unsafe because left-trimming
    136   // re-pushes only grey arrays onto the marking worklist.
    137   DCHECK(!obj->IsFixedArrayBase());
    138   // Color the object black and push it into the bailout deque.
    139   marking_state()->WhiteToGrey(obj);
    140   if (marking_state()->GreyToBlack(obj)) {
    141     if (FLAG_concurrent_marking) {
    142       marking_worklist()->PushBailout(obj);
    143     } else {
    144       marking_worklist()->Push(obj);
    145     }
    146   }
    147 }
    148 
    149 void IncrementalMarking::NotifyLeftTrimming(HeapObject* from, HeapObject* to) {
    150   DCHECK(IsMarking());
    151   DCHECK(MemoryChunk::FromAddress(from->address())->SweepingDone());
    152   DCHECK_EQ(MemoryChunk::FromAddress(from->address()),
    153             MemoryChunk::FromAddress(to->address()));
    154   DCHECK_NE(from, to);
    155 
    156   MarkBit old_mark_bit = marking_state()->MarkBitFrom(from);
    157   MarkBit new_mark_bit = marking_state()->MarkBitFrom(to);
    158 
    159   if (black_allocation() && Marking::IsBlack<kAtomicity>(new_mark_bit)) {
    160     // Nothing to do if the object is in black area.
    161     return;
    162   }
    163 
    164   bool marked_black_due_to_left_trimming = false;
    165   if (FLAG_concurrent_marking) {
    166     // We need to mark the array black before overwriting its map and length
    167     // so that the concurrent marker does not observe inconsistent state.
    168     Marking::WhiteToGrey<kAtomicity>(old_mark_bit);
    169     if (Marking::GreyToBlack<kAtomicity>(old_mark_bit)) {
    170       // The concurrent marker will not mark the array. We need to push the
    171       // new array start in marking deque to ensure that it will be marked.
    172       marked_black_due_to_left_trimming = true;
    173     }
    174     DCHECK(Marking::IsBlack<kAtomicity>(old_mark_bit));
    175   }
    176 
    177   if (Marking::IsBlack<kAtomicity>(old_mark_bit) &&
    178       !marked_black_due_to_left_trimming) {
    179     // The array was black before left trimming or was marked black by the
    180     // concurrent marker. Simply transfer the color.
    181     if (from->address() + kPointerSize == to->address()) {
    182       // The old and the new markbits overlap. The |to| object has the
    183       // grey color. To make it black, we need to set the second bit.
    184       DCHECK(new_mark_bit.Get<kAtomicity>());
    185       new_mark_bit.Next().Set<kAtomicity>();
    186     } else {
    187       bool success = Marking::WhiteToBlack<kAtomicity>(new_mark_bit);
    188       DCHECK(success);
    189       USE(success);
    190     }
    191   } else if (Marking::IsGrey<kAtomicity>(old_mark_bit) ||
    192              marked_black_due_to_left_trimming) {
    193     // The array was already grey or was marked black by this function.
    194     // Mark the new array grey and push it to marking deque.
    195     if (from->address() + kPointerSize == to->address()) {
    196       // The old and the new markbits overlap. The |to| object is either white
    197       // or grey.  Set the first bit to make sure that it is grey.
    198       new_mark_bit.Set<kAtomicity>();
    199       DCHECK(!new_mark_bit.Next().Get<kAtomicity>());
    200     } else {
    201       bool success = Marking::WhiteToGrey<kAtomicity>(new_mark_bit);
    202       DCHECK(success);
    203       USE(success);
    204     }
    205     // Subsequent left-trimming will re-push only grey arrays.
    206     // Ensure that this array is grey.
    207     DCHECK(Marking::IsGrey<kAtomicity>(new_mark_bit));
    208     marking_worklist()->PushBailout(to);
    209     RestartIfNotMarking();
    210   }
    211 }
    212 
    213 class IncrementalMarkingRootMarkingVisitor : public RootVisitor {
    214  public:
    215   explicit IncrementalMarkingRootMarkingVisitor(
    216       IncrementalMarking* incremental_marking)
    217       : heap_(incremental_marking->heap()) {}
    218 
    219   void VisitRootPointer(Root root, const char* description,
    220                         Object** p) override {
    221     MarkObjectByPointer(p);
    222   }
    223 
    224   void VisitRootPointers(Root root, const char* description, Object** start,
    225                          Object** end) override {
    226     for (Object** p = start; p < end; p++) MarkObjectByPointer(p);
    227   }
    228 
    229  private:
    230   void MarkObjectByPointer(Object** p) {
    231     Object* obj = *p;
    232     if (!obj->IsHeapObject()) return;
    233 
    234     heap_->incremental_marking()->WhiteToGreyAndPush(HeapObject::cast(obj));
    235   }
    236 
    237   Heap* heap_;
    238 };
    239 
    240 void IncrementalMarking::DeactivateIncrementalWriteBarrierForSpace(
    241     PagedSpace* space) {
    242   for (Page* p : *space) {
    243     p->SetOldGenerationPageFlags(false);
    244   }
    245 }
    246 
    247 
    248 void IncrementalMarking::DeactivateIncrementalWriteBarrierForSpace(
    249     NewSpace* space) {
    250   for (Page* p : *space) {
    251     p->SetYoungGenerationPageFlags(false);
    252   }
    253 }
    254 
    255 
    256 void IncrementalMarking::DeactivateIncrementalWriteBarrier() {
    257   DeactivateIncrementalWriteBarrierForSpace(heap_->old_space());
    258   DeactivateIncrementalWriteBarrierForSpace(heap_->map_space());
    259   DeactivateIncrementalWriteBarrierForSpace(heap_->code_space());
    260   DeactivateIncrementalWriteBarrierForSpace(heap_->new_space());
    261 
    262   for (LargePage* p : *heap_->lo_space()) {
    263     p->SetOldGenerationPageFlags(false);
    264   }
    265 }
    266 
    267 
    268 void IncrementalMarking::ActivateIncrementalWriteBarrier(PagedSpace* space) {
    269   for (Page* p : *space) {
    270     p->SetOldGenerationPageFlags(true);
    271   }
    272 }
    273 
    274 
    275 void IncrementalMarking::ActivateIncrementalWriteBarrier(NewSpace* space) {
    276   for (Page* p : *space) {
    277     p->SetYoungGenerationPageFlags(true);
    278   }
    279 }
    280 
    281 
    282 void IncrementalMarking::ActivateIncrementalWriteBarrier() {
    283   ActivateIncrementalWriteBarrier(heap_->old_space());
    284   ActivateIncrementalWriteBarrier(heap_->map_space());
    285   ActivateIncrementalWriteBarrier(heap_->code_space());
    286   ActivateIncrementalWriteBarrier(heap_->new_space());
    287 
    288   for (LargePage* p : *heap_->lo_space()) {
    289     p->SetOldGenerationPageFlags(true);
    290   }
    291 }
    292 
    293 
    294 bool IncrementalMarking::WasActivated() { return was_activated_; }
    295 
    296 
    297 bool IncrementalMarking::CanBeActivated() {
    298   // Only start incremental marking in a safe state: 1) when incremental
    299   // marking is turned on, 2) when we are currently not in a GC, and
    300   // 3) when we are currently not serializing or deserializing the heap.
    301   return FLAG_incremental_marking && heap_->gc_state() == Heap::NOT_IN_GC &&
    302          heap_->deserialization_complete() &&
    303          !heap_->isolate()->serializer_enabled();
    304 }
    305 
    306 
    307 void IncrementalMarking::Deactivate() {
    308   DeactivateIncrementalWriteBarrier();
    309 }
    310 
    311 void IncrementalMarking::Start(GarbageCollectionReason gc_reason) {
    312   if (FLAG_trace_incremental_marking) {
    313     int old_generation_size_mb =
    314         static_cast<int>(heap()->OldGenerationSizeOfObjects() / MB);
    315     int old_generation_limit_mb =
    316         static_cast<int>(heap()->old_generation_allocation_limit() / MB);
    317     heap()->isolate()->PrintWithTimestamp(
    318         "[IncrementalMarking] Start (%s): old generation %dMB, limit %dMB, "
    319         "slack %dMB\n",
    320         Heap::GarbageCollectionReasonToString(gc_reason),
    321         old_generation_size_mb, old_generation_limit_mb,
    322         Max(0, old_generation_limit_mb - old_generation_size_mb));
    323   }
    324   DCHECK(FLAG_incremental_marking);
    325   DCHECK(state_ == STOPPED);
    326   DCHECK(heap_->gc_state() == Heap::NOT_IN_GC);
    327   DCHECK(!heap_->isolate()->serializer_enabled());
    328 
    329   Counters* counters = heap_->isolate()->counters();
    330 
    331   counters->incremental_marking_reason()->AddSample(
    332       static_cast<int>(gc_reason));
    333   HistogramTimerScope incremental_marking_scope(
    334       counters->gc_incremental_marking_start());
    335   TRACE_EVENT0("v8", "V8.GCIncrementalMarkingStart");
    336   TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_INCREMENTAL_START);
    337   heap_->tracer()->NotifyIncrementalMarkingStart();
    338 
    339   start_time_ms_ = heap()->MonotonicallyIncreasingTimeInMs();
    340   initial_old_generation_size_ = heap_->OldGenerationSizeOfObjects();
    341   old_generation_allocation_counter_ = heap_->OldGenerationAllocationCounter();
    342   bytes_allocated_ = 0;
    343   bytes_marked_ahead_of_schedule_ = 0;
    344   bytes_marked_concurrently_ = 0;
    345   should_hurry_ = false;
    346   was_activated_ = true;
    347 
    348   if (!heap_->mark_compact_collector()->sweeping_in_progress()) {
    349     StartMarking();
    350   } else {
    351     if (FLAG_trace_incremental_marking) {
    352       heap()->isolate()->PrintWithTimestamp(
    353           "[IncrementalMarking] Start sweeping.\n");
    354     }
    355     SetState(SWEEPING);
    356   }
    357 
    358   heap_->AddAllocationObserversToAllSpaces(&old_generation_observer_,
    359                                            &new_generation_observer_);
    360   incremental_marking_job()->Start(heap_);
    361 }
    362 
    363 
    364 void IncrementalMarking::StartMarking() {
    365   if (heap_->isolate()->serializer_enabled()) {
    366     // Black allocation currently starts when we start incremental marking,
    367     // but we cannot enable black allocation while deserializing. Hence, we
    368     // have to delay the start of incremental marking in that case.
    369     if (FLAG_trace_incremental_marking) {
    370       heap()->isolate()->PrintWithTimestamp(
    371           "[IncrementalMarking] Start delayed - serializer\n");
    372     }
    373     return;
    374   }
    375   if (FLAG_trace_incremental_marking) {
    376     heap()->isolate()->PrintWithTimestamp(
    377         "[IncrementalMarking] Start marking\n");
    378   }
    379 
    380   is_compacting_ =
    381       !FLAG_never_compact && heap_->mark_compact_collector()->StartCompaction();
    382 
    383   SetState(MARKING);
    384 
    385   {
    386     TRACE_GC(heap()->tracer(),
    387              GCTracer::Scope::MC_INCREMENTAL_WRAPPER_PROLOGUE);
    388     heap_->local_embedder_heap_tracer()->TracePrologue();
    389   }
    390 
    391   ActivateIncrementalWriteBarrier();
    392 
    393 // Marking bits are cleared by the sweeper.
    394 #ifdef VERIFY_HEAP
    395   if (FLAG_verify_heap) {
    396     heap_->mark_compact_collector()->VerifyMarkbitsAreClean();
    397   }
    398 #endif
    399 
    400   heap_->isolate()->compilation_cache()->MarkCompactPrologue();
    401 
    402 #ifdef V8_CONCURRENT_MARKING
    403   // The write-barrier does not check the color of the source object.
    404   // Start black allocation earlier to ensure faster marking progress.
    405   if (!black_allocation_) {
    406     StartBlackAllocation();
    407   }
    408 #endif
    409 
    410   // Mark strong roots grey.
    411   IncrementalMarkingRootMarkingVisitor visitor(this);
    412   heap_->IterateStrongRoots(&visitor, VISIT_ONLY_STRONG);
    413 
    414   if (FLAG_concurrent_marking && !heap_->IsTearingDown()) {
    415     heap_->concurrent_marking()->ScheduleTasks();
    416   }
    417 
    418   // Ready to start incremental marking.
    419   if (FLAG_trace_incremental_marking) {
    420     heap()->isolate()->PrintWithTimestamp("[IncrementalMarking] Running\n");
    421   }
    422 }
    423 
    424 void IncrementalMarking::StartBlackAllocation() {
    425   DCHECK(FLAG_black_allocation);
    426   DCHECK(!black_allocation_);
    427   DCHECK(IsMarking());
    428   black_allocation_ = true;
    429   heap()->old_space()->MarkLinearAllocationAreaBlack();
    430   heap()->map_space()->MarkLinearAllocationAreaBlack();
    431   heap()->code_space()->MarkLinearAllocationAreaBlack();
    432   if (FLAG_trace_incremental_marking) {
    433     heap()->isolate()->PrintWithTimestamp(
    434         "[IncrementalMarking] Black allocation started\n");
    435   }
    436 }
    437 
    438 void IncrementalMarking::PauseBlackAllocation() {
    439   DCHECK(FLAG_black_allocation);
    440   DCHECK(IsMarking());
    441   heap()->old_space()->UnmarkLinearAllocationArea();
    442   heap()->map_space()->UnmarkLinearAllocationArea();
    443   heap()->code_space()->UnmarkLinearAllocationArea();
    444   if (FLAG_trace_incremental_marking) {
    445     heap()->isolate()->PrintWithTimestamp(
    446         "[IncrementalMarking] Black allocation paused\n");
    447   }
    448   black_allocation_ = false;
    449 }
    450 
    451 void IncrementalMarking::FinishBlackAllocation() {
    452   if (black_allocation_) {
    453     black_allocation_ = false;
    454     if (FLAG_trace_incremental_marking) {
    455       heap()->isolate()->PrintWithTimestamp(
    456           "[IncrementalMarking] Black allocation finished\n");
    457     }
    458   }
    459 }
    460 
    461 void IncrementalMarking::AbortBlackAllocation() {
    462   if (FLAG_trace_incremental_marking) {
    463     heap()->isolate()->PrintWithTimestamp(
    464         "[IncrementalMarking] Black allocation aborted\n");
    465   }
    466 }
    467 
    468 void IncrementalMarking::MarkRoots() {
    469   DCHECK(!finalize_marking_completed_);
    470   DCHECK(IsMarking());
    471 
    472   IncrementalMarkingRootMarkingVisitor visitor(this);
    473   heap_->IterateStrongRoots(&visitor, VISIT_ONLY_STRONG);
    474 }
    475 
    476 bool IncrementalMarking::ShouldRetainMap(Map* map, int age) {
    477   if (age == 0) {
    478     // The map has aged. Do not retain this map.
    479     return false;
    480   }
    481   Object* constructor = map->GetConstructor();
    482   if (!constructor->IsHeapObject() ||
    483       marking_state()->IsWhite(HeapObject::cast(constructor))) {
    484     // The constructor is dead, no new objects with this map can
    485     // be created. Do not retain this map.
    486     return false;
    487   }
    488   return true;
    489 }
    490 
    491 
    492 void IncrementalMarking::RetainMaps() {
    493   // Do not retain dead maps if flag disables it or there is
    494   // - memory pressure (reduce_memory_footprint_),
    495   // - GC is requested by tests or dev-tools (abort_incremental_marking_).
    496   bool map_retaining_is_disabled = heap()->ShouldReduceMemory() ||
    497                                    heap()->ShouldAbortIncrementalMarking() ||
    498                                    FLAG_retain_maps_for_n_gc == 0;
    499   WeakArrayList* retained_maps = heap()->retained_maps();
    500   int length = retained_maps->length();
    501   // The number_of_disposed_maps separates maps in the retained_maps
    502   // array that were created before and after context disposal.
    503   // We do not age and retain disposed maps to avoid memory leaks.
    504   int number_of_disposed_maps = heap()->number_of_disposed_maps_;
    505   for (int i = 0; i < length; i += 2) {
    506     MaybeObject* value = retained_maps->Get(i);
    507     HeapObject* map_heap_object;
    508     if (!value->ToWeakHeapObject(&map_heap_object)) {
    509       continue;
    510     }
    511     int age = Smi::ToInt(retained_maps->Get(i + 1)->ToSmi());
    512     int new_age;
    513     Map* map = Map::cast(map_heap_object);
    514     if (i >= number_of_disposed_maps && !map_retaining_is_disabled &&
    515         marking_state()->IsWhite(map)) {
    516       if (ShouldRetainMap(map, age)) {
    517         WhiteToGreyAndPush(map);
    518       }
    519       Object* prototype = map->prototype();
    520       if (age > 0 && prototype->IsHeapObject() &&
    521           marking_state()->IsWhite(HeapObject::cast(prototype))) {
    522         // The prototype is not marked, age the map.
    523         new_age = age - 1;
    524       } else {
    525         // The prototype and the constructor are marked, this map keeps only
    526         // transition tree alive, not JSObjects. Do not age the map.
    527         new_age = age;
    528       }
    529     } else {
    530       new_age = FLAG_retain_maps_for_n_gc;
    531     }
    532     // Compact the array and update the age.
    533     if (new_age != age) {
    534       retained_maps->Set(i + 1, MaybeObject::FromSmi(Smi::FromInt(new_age)));
    535     }
    536   }
    537 }
    538 
    539 void IncrementalMarking::FinalizeIncrementally() {
    540   TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_INCREMENTAL_FINALIZE_BODY);
    541   DCHECK(!finalize_marking_completed_);
    542   DCHECK(IsMarking());
    543 
    544   double start = heap_->MonotonicallyIncreasingTimeInMs();
    545 
    546   // After finishing incremental marking, we try to discover all unmarked
    547   // objects to reduce the marking load in the final pause.
    548   // 1) We scan and mark the roots again to find all changes to the root set.
    549   // 2) Age and retain maps embedded in optimized code.
    550   MarkRoots();
    551 
    552   // Map retaining is needed for perfromance, not correctness,
    553   // so we can do it only once at the beginning of the finalization.
    554   RetainMaps();
    555 
    556   finalize_marking_completed_ = true;
    557 
    558   if (FLAG_black_allocation && !heap()->ShouldReduceMemory() &&
    559       !black_allocation_) {
    560     // TODO(hpayer): Move to an earlier point as soon as we make faster marking
    561     // progress.
    562     StartBlackAllocation();
    563   }
    564 
    565   if (FLAG_trace_incremental_marking) {
    566     double end = heap_->MonotonicallyIncreasingTimeInMs();
    567     double delta = end - start;
    568     heap()->isolate()->PrintWithTimestamp(
    569         "[IncrementalMarking] Finalize incrementally spent %.1f ms.\n", delta);
    570   }
    571 }
    572 
    573 void IncrementalMarking::UpdateMarkingWorklistAfterScavenge() {
    574   if (!IsMarking()) return;
    575 
    576   Map* filler_map = ReadOnlyRoots(heap_).one_pointer_filler_map();
    577 
    578 #ifdef ENABLE_MINOR_MC
    579   MinorMarkCompactCollector::MarkingState* minor_marking_state =
    580       heap()->minor_mark_compact_collector()->marking_state();
    581 #else
    582   void* minor_marking_state = nullptr;
    583 #endif  // ENABLE_MINOR_MC
    584 
    585   marking_worklist()->Update([this, filler_map, minor_marking_state](
    586                                  HeapObject* obj, HeapObject** out) -> bool {
    587     DCHECK(obj->IsHeapObject());
    588     // Only pointers to from space have to be updated.
    589     if (Heap::InFromSpace(obj)) {
    590       MapWord map_word = obj->map_word();
    591       if (!map_word.IsForwardingAddress()) {
    592         // There may be objects on the marking deque that do not exist anymore,
    593         // e.g. left trimmed objects or objects from the root set (frames).
    594         // If these object are dead at scavenging time, their marking deque
    595         // entries will not point to forwarding addresses. Hence, we can discard
    596         // them.
    597         return false;
    598       }
    599       HeapObject* dest = map_word.ToForwardingAddress();
    600       DCHECK_IMPLIES(marking_state()->IsWhite(obj), obj->IsFiller());
    601       *out = dest;
    602       return true;
    603     } else if (Heap::InToSpace(obj)) {
    604       // The object may be on a page that was moved in new space.
    605       DCHECK(
    606           Page::FromAddress(obj->address())->IsFlagSet(Page::SWEEP_TO_ITERATE));
    607 #ifdef ENABLE_MINOR_MC
    608       if (minor_marking_state->IsGrey(obj)) {
    609         *out = obj;
    610         return true;
    611       }
    612 #endif  // ENABLE_MINOR_MC
    613       return false;
    614     } else {
    615       // The object may be on a page that was moved from new to old space. Only
    616       // applicable during minor MC garbage collections.
    617       if (Page::FromAddress(obj->address())
    618               ->IsFlagSet(Page::SWEEP_TO_ITERATE)) {
    619 #ifdef ENABLE_MINOR_MC
    620         if (minor_marking_state->IsGrey(obj)) {
    621           *out = obj;
    622           return true;
    623         }
    624 #endif  // ENABLE_MINOR_MC
    625         return false;
    626       }
    627       DCHECK_IMPLIES(marking_state()->IsWhite(obj), obj->IsFiller());
    628       // Skip one word filler objects that appear on the
    629       // stack when we perform in place array shift.
    630       if (obj->map() != filler_map) {
    631         *out = obj;
    632         return true;
    633       }
    634       return false;
    635     }
    636   });
    637 
    638   UpdateWeakReferencesAfterScavenge();
    639 }
    640 
    641 namespace {
    642 template <typename T>
    643 T* ForwardingAddress(T* heap_obj) {
    644   MapWord map_word = heap_obj->map_word();
    645 
    646   if (map_word.IsForwardingAddress()) {
    647     return T::cast(map_word.ToForwardingAddress());
    648   } else if (Heap::InNewSpace(heap_obj)) {
    649     return nullptr;
    650   } else {
    651     return heap_obj;
    652   }
    653 }
    654 }  // namespace
    655 
    656 void IncrementalMarking::UpdateWeakReferencesAfterScavenge() {
    657   weak_objects_->weak_references.Update(
    658       [](std::pair<HeapObject*, HeapObjectReference**> slot_in,
    659          std::pair<HeapObject*, HeapObjectReference**>* slot_out) -> bool {
    660         HeapObject* heap_obj = slot_in.first;
    661         HeapObject* forwarded = ForwardingAddress(heap_obj);
    662 
    663         if (forwarded) {
    664           ptrdiff_t distance_to_slot =
    665               reinterpret_cast<Address>(slot_in.second) -
    666               reinterpret_cast<Address>(slot_in.first);
    667           Address new_slot =
    668               reinterpret_cast<Address>(forwarded) + distance_to_slot;
    669           slot_out->first = forwarded;
    670           slot_out->second = reinterpret_cast<HeapObjectReference**>(new_slot);
    671           return true;
    672         }
    673 
    674         return false;
    675       });
    676   weak_objects_->weak_objects_in_code.Update(
    677       [](std::pair<HeapObject*, Code*> slot_in,
    678          std::pair<HeapObject*, Code*>* slot_out) -> bool {
    679         HeapObject* heap_obj = slot_in.first;
    680         HeapObject* forwarded = ForwardingAddress(heap_obj);
    681 
    682         if (forwarded) {
    683           slot_out->first = forwarded;
    684           slot_out->second = slot_in.second;
    685           return true;
    686         }
    687 
    688         return false;
    689       });
    690   weak_objects_->ephemeron_hash_tables.Update(
    691       [](EphemeronHashTable* slot_in, EphemeronHashTable** slot_out) -> bool {
    692         EphemeronHashTable* forwarded = ForwardingAddress(slot_in);
    693 
    694         if (forwarded) {
    695           *slot_out = forwarded;
    696           return true;
    697         }
    698 
    699         return false;
    700       });
    701 
    702   auto ephemeron_updater = [](Ephemeron slot_in, Ephemeron* slot_out) -> bool {
    703     HeapObject* key = slot_in.key;
    704     HeapObject* value = slot_in.value;
    705     HeapObject* forwarded_key = ForwardingAddress(key);
    706     HeapObject* forwarded_value = ForwardingAddress(value);
    707 
    708     if (forwarded_key && forwarded_value) {
    709       *slot_out = Ephemeron{forwarded_key, forwarded_value};
    710       return true;
    711     }
    712 
    713     return false;
    714   };
    715 
    716   weak_objects_->current_ephemerons.Update(ephemeron_updater);
    717   weak_objects_->next_ephemerons.Update(ephemeron_updater);
    718   weak_objects_->discovered_ephemerons.Update(ephemeron_updater);
    719 }
    720 
    721 void IncrementalMarking::UpdateMarkedBytesAfterScavenge(
    722     size_t dead_bytes_in_new_space) {
    723   if (!IsMarking()) return;
    724   bytes_marked_ahead_of_schedule_ -=
    725       Min(bytes_marked_ahead_of_schedule_, dead_bytes_in_new_space);
    726 }
    727 
    728 bool IncrementalMarking::IsFixedArrayWithProgressBar(HeapObject* obj) {
    729   if (!obj->IsFixedArray()) return false;
    730   MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address());
    731   return chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR);
    732 }
    733 
    734 int IncrementalMarking::VisitObject(Map* map, HeapObject* obj) {
    735   DCHECK(marking_state()->IsGrey(obj) || marking_state()->IsBlack(obj));
    736   if (!marking_state()->GreyToBlack(obj)) {
    737     // The object can already be black in these cases:
    738     // 1. The object is a fixed array with the progress bar.
    739     // 2. The object is a JSObject that was colored black before
    740     //    unsafe layout change.
    741     // 3. The object is a string that was colored black before
    742     //    unsafe layout change.
    743     // 4. The object is materizalized by the deoptimizer.
    744     DCHECK(obj->IsHashTable() || obj->IsPropertyArray() ||
    745            obj->IsFixedArray() || obj->IsJSObject() || obj->IsString());
    746   }
    747   DCHECK(marking_state()->IsBlack(obj));
    748   WhiteToGreyAndPush(map);
    749   IncrementalMarkingMarkingVisitor visitor(heap()->mark_compact_collector(),
    750                                            marking_state());
    751   return visitor.Visit(map, obj);
    752 }
    753 
    754 void IncrementalMarking::ProcessBlackAllocatedObject(HeapObject* obj) {
    755   if (IsMarking() && marking_state()->IsBlack(obj)) {
    756     RevisitObject(obj);
    757   }
    758 }
    759 
    760 void IncrementalMarking::RevisitObject(HeapObject* obj) {
    761   DCHECK(IsMarking());
    762   DCHECK(FLAG_concurrent_marking || marking_state()->IsBlack(obj));
    763   Page* page = Page::FromAddress(obj->address());
    764   if (page->owner()->identity() == LO_SPACE) {
    765     page->ResetProgressBar();
    766   }
    767   Map* map = obj->map();
    768   WhiteToGreyAndPush(map);
    769   IncrementalMarkingMarkingVisitor visitor(heap()->mark_compact_collector(),
    770                                            marking_state());
    771   visitor.Visit(map, obj);
    772 }
    773 
    774 template <WorklistToProcess worklist_to_process>
    775 intptr_t IncrementalMarking::ProcessMarkingWorklist(
    776     intptr_t bytes_to_process, ForceCompletionAction completion) {
    777   intptr_t bytes_processed = 0;
    778   while (bytes_processed < bytes_to_process || completion == FORCE_COMPLETION) {
    779     HeapObject* obj;
    780     if (worklist_to_process == WorklistToProcess::kBailout) {
    781       obj = marking_worklist()->PopBailout();
    782     } else {
    783       obj = marking_worklist()->Pop();
    784     }
    785     if (obj == nullptr) break;
    786     // Left trimming may result in white, grey, or black filler objects on the
    787     // marking deque. Ignore these objects.
    788     if (obj->IsFiller()) {
    789       DCHECK(!marking_state()->IsImpossible(obj));
    790       continue;
    791     }
    792     unscanned_bytes_of_large_object_ = 0;
    793     int size = VisitObject(obj->map(), obj);
    794     bytes_processed += size - unscanned_bytes_of_large_object_;
    795   }
    796   // Report all found wrappers to the embedder. This is necessary as the
    797   // embedder could potentially invalidate wrappers as soon as V8 is done
    798   // with its incremental marking processing. Any cached wrappers could
    799   // result in broken pointers at this point.
    800   heap_->local_embedder_heap_tracer()->RegisterWrappersWithRemoteTracer();
    801   return bytes_processed;
    802 }
    803 
    804 
    805 void IncrementalMarking::Hurry() {
    806   // A scavenge may have pushed new objects on the marking deque (due to black
    807   // allocation) even in COMPLETE state. This may happen if scavenges are
    808   // forced e.g. in tests. It should not happen when COMPLETE was set when
    809   // incremental marking finished and a regular GC was triggered after that
    810   // because should_hurry_ will force a full GC.
    811   if (!marking_worklist()->IsEmpty()) {
    812     double start = 0.0;
    813     if (FLAG_trace_incremental_marking) {
    814       start = heap_->MonotonicallyIncreasingTimeInMs();
    815       if (FLAG_trace_incremental_marking) {
    816         heap()->isolate()->PrintWithTimestamp("[IncrementalMarking] Hurry\n");
    817       }
    818     }
    819     // TODO(gc) hurry can mark objects it encounters black as mutator
    820     // was stopped.
    821     ProcessMarkingWorklist(0, FORCE_COMPLETION);
    822     SetState(COMPLETE);
    823     if (FLAG_trace_incremental_marking) {
    824       double end = heap_->MonotonicallyIncreasingTimeInMs();
    825       double delta = end - start;
    826       if (FLAG_trace_incremental_marking) {
    827         heap()->isolate()->PrintWithTimestamp(
    828             "[IncrementalMarking] Complete (hurry), spent %d ms.\n",
    829             static_cast<int>(delta));
    830       }
    831     }
    832   }
    833 }
    834 
    835 
    836 void IncrementalMarking::Stop() {
    837   if (IsStopped()) return;
    838   if (FLAG_trace_incremental_marking) {
    839     int old_generation_size_mb =
    840         static_cast<int>(heap()->OldGenerationSizeOfObjects() / MB);
    841     int old_generation_limit_mb =
    842         static_cast<int>(heap()->old_generation_allocation_limit() / MB);
    843     heap()->isolate()->PrintWithTimestamp(
    844         "[IncrementalMarking] Stopping: old generation %dMB, limit %dMB, "
    845         "overshoot %dMB\n",
    846         old_generation_size_mb, old_generation_limit_mb,
    847         Max(0, old_generation_size_mb - old_generation_limit_mb));
    848   }
    849 
    850   SpaceIterator it(heap_);
    851   while (it.has_next()) {
    852     Space* space = it.next();
    853     if (space == heap_->new_space()) {
    854       space->RemoveAllocationObserver(&new_generation_observer_);
    855     } else {
    856       space->RemoveAllocationObserver(&old_generation_observer_);
    857     }
    858   }
    859 
    860   IncrementalMarking::set_should_hurry(false);
    861   heap_->isolate()->stack_guard()->ClearGC();
    862   SetState(STOPPED);
    863   is_compacting_ = false;
    864   FinishBlackAllocation();
    865 }
    866 
    867 
    868 void IncrementalMarking::Finalize() {
    869   Hurry();
    870   Stop();
    871 }
    872 
    873 
    874 void IncrementalMarking::FinalizeMarking(CompletionAction action) {
    875   DCHECK(!finalize_marking_completed_);
    876   if (FLAG_trace_incremental_marking) {
    877     heap()->isolate()->PrintWithTimestamp(
    878         "[IncrementalMarking] requesting finalization of incremental "
    879         "marking.\n");
    880   }
    881   request_type_ = FINALIZATION;
    882   if (action == GC_VIA_STACK_GUARD) {
    883     heap_->isolate()->stack_guard()->RequestGC();
    884   }
    885 }
    886 
    887 
    888 void IncrementalMarking::MarkingComplete(CompletionAction action) {
    889   SetState(COMPLETE);
    890   // We will set the stack guard to request a GC now.  This will mean the rest
    891   // of the GC gets performed as soon as possible (we can't do a GC here in a
    892   // record-write context).  If a few things get allocated between now and then
    893   // that shouldn't make us do a scavenge and keep being incremental, so we set
    894   // the should-hurry flag to indicate that there can't be much work left to do.
    895   set_should_hurry(true);
    896   if (FLAG_trace_incremental_marking) {
    897     heap()->isolate()->PrintWithTimestamp(
    898         "[IncrementalMarking] Complete (normal).\n");
    899   }
    900   request_type_ = COMPLETE_MARKING;
    901   if (action == GC_VIA_STACK_GUARD) {
    902     heap_->isolate()->stack_guard()->RequestGC();
    903   }
    904 }
    905 
    906 
    907 void IncrementalMarking::Epilogue() {
    908   was_activated_ = false;
    909   finalize_marking_completed_ = false;
    910 }
    911 
    912 double IncrementalMarking::AdvanceIncrementalMarking(
    913     double deadline_in_ms, CompletionAction completion_action,
    914     StepOrigin step_origin) {
    915   HistogramTimerScope incremental_marking_scope(
    916       heap_->isolate()->counters()->gc_incremental_marking());
    917   TRACE_EVENT0("v8", "V8.GCIncrementalMarking");
    918   TRACE_GC(heap_->tracer(), GCTracer::Scope::MC_INCREMENTAL);
    919   DCHECK(!IsStopped());
    920   DCHECK_EQ(
    921       0, heap_->local_embedder_heap_tracer()->NumberOfCachedWrappersToTrace());
    922 
    923   double remaining_time_in_ms = 0.0;
    924   intptr_t step_size_in_bytes = GCIdleTimeHandler::EstimateMarkingStepSize(
    925       kStepSizeInMs,
    926       heap()->tracer()->IncrementalMarkingSpeedInBytesPerMillisecond());
    927 
    928   const bool incremental_wrapper_tracing =
    929       state_ == MARKING && FLAG_incremental_marking_wrappers &&
    930       heap_->local_embedder_heap_tracer()->InUse();
    931   do {
    932     if (incremental_wrapper_tracing && trace_wrappers_toggle_) {
    933       TRACE_GC(heap()->tracer(),
    934                GCTracer::Scope::MC_INCREMENTAL_WRAPPER_TRACING);
    935       const double wrapper_deadline =
    936           heap_->MonotonicallyIncreasingTimeInMs() + kStepSizeInMs;
    937       if (!heap_->local_embedder_heap_tracer()
    938                ->ShouldFinalizeIncrementalMarking()) {
    939         heap_->local_embedder_heap_tracer()->Trace(wrapper_deadline);
    940       }
    941     } else {
    942       Step(step_size_in_bytes, completion_action, step_origin);
    943     }
    944     trace_wrappers_toggle_ = !trace_wrappers_toggle_;
    945     remaining_time_in_ms =
    946         deadline_in_ms - heap()->MonotonicallyIncreasingTimeInMs();
    947   } while (remaining_time_in_ms >= kStepSizeInMs && !IsComplete() &&
    948            !marking_worklist()->IsEmpty());
    949   return remaining_time_in_ms;
    950 }
    951 
    952 
    953 void IncrementalMarking::FinalizeSweeping() {
    954   DCHECK(state_ == SWEEPING);
    955   if (heap_->mark_compact_collector()->sweeping_in_progress() &&
    956       (!FLAG_concurrent_sweeping ||
    957        !heap_->mark_compact_collector()->sweeper()->AreSweeperTasksRunning())) {
    958     heap_->mark_compact_collector()->EnsureSweepingCompleted();
    959   }
    960   if (!heap_->mark_compact_collector()->sweeping_in_progress()) {
    961 #ifdef DEBUG
    962     heap_->VerifyCountersAfterSweeping();
    963 #endif
    964     StartMarking();
    965   }
    966 }
    967 
    968 size_t IncrementalMarking::StepSizeToKeepUpWithAllocations() {
    969   // Update bytes_allocated_ based on the allocation counter.
    970   size_t current_counter = heap_->OldGenerationAllocationCounter();
    971   bytes_allocated_ += current_counter - old_generation_allocation_counter_;
    972   old_generation_allocation_counter_ = current_counter;
    973   return bytes_allocated_;
    974 }
    975 
    976 size_t IncrementalMarking::StepSizeToMakeProgress() {
    977   // We increase step size gradually based on the time passed in order to
    978   // leave marking work to standalone tasks. The ramp up duration and the
    979   // target step count are chosen based on benchmarks.
    980   const int kRampUpIntervalMs = 300;
    981   const size_t kTargetStepCount = 256;
    982   const size_t kTargetStepCountAtOOM = 32;
    983   size_t oom_slack = heap()->new_space()->Capacity() + 64 * MB;
    984 
    985   if (!heap()->CanExpandOldGeneration(oom_slack)) {
    986     return heap()->OldGenerationSizeOfObjects() / kTargetStepCountAtOOM;
    987   }
    988 
    989   size_t step_size = Max(initial_old_generation_size_ / kTargetStepCount,
    990                          IncrementalMarking::kMinStepSizeInBytes);
    991   double time_passed_ms =
    992       heap_->MonotonicallyIncreasingTimeInMs() - start_time_ms_;
    993   double factor = Min(time_passed_ms / kRampUpIntervalMs, 1.0);
    994   return static_cast<size_t>(factor * step_size);
    995 }
    996 
    997 void IncrementalMarking::AdvanceIncrementalMarkingOnAllocation() {
    998   // Code using an AlwaysAllocateScope assumes that the GC state does not
    999   // change; that implies that no marking steps must be performed.
   1000   if (heap_->gc_state() != Heap::NOT_IN_GC || !FLAG_incremental_marking ||
   1001       (state_ != SWEEPING && state_ != MARKING) || heap_->always_allocate()) {
   1002     return;
   1003   }
   1004 
   1005   size_t bytes_to_process =
   1006       StepSizeToKeepUpWithAllocations() + StepSizeToMakeProgress();
   1007 
   1008   if (bytes_to_process >= IncrementalMarking::kMinStepSizeInBytes) {
   1009     HistogramTimerScope incremental_marking_scope(
   1010         heap_->isolate()->counters()->gc_incremental_marking());
   1011     TRACE_EVENT0("v8", "V8.GCIncrementalMarking");
   1012     TRACE_GC(heap_->tracer(), GCTracer::Scope::MC_INCREMENTAL);
   1013     // The first step after Scavenge will see many allocated bytes.
   1014     // Cap the step size to distribute the marking work more uniformly.
   1015     size_t max_step_size = GCIdleTimeHandler::EstimateMarkingStepSize(
   1016         kMaxStepSizeInMs,
   1017         heap()->tracer()->IncrementalMarkingSpeedInBytesPerMillisecond());
   1018     bytes_to_process = Min(bytes_to_process, max_step_size);
   1019     size_t bytes_processed = 0;
   1020     if (FLAG_concurrent_marking) {
   1021       bytes_processed = Step(bytes_to_process, GC_VIA_STACK_GUARD,
   1022                              StepOrigin::kV8, WorklistToProcess::kBailout);
   1023       bytes_to_process = (bytes_processed >= bytes_to_process)
   1024                              ? 0
   1025                              : bytes_to_process - bytes_processed;
   1026       size_t current_bytes_marked_concurrently =
   1027           heap()->concurrent_marking()->TotalMarkedBytes();
   1028       // The concurrent_marking()->TotalMarkedBytes() is not monothonic for a
   1029       // short period of time when a concurrent marking task is finishing.
   1030       if (current_bytes_marked_concurrently > bytes_marked_concurrently_) {
   1031         bytes_marked_ahead_of_schedule_ +=
   1032             current_bytes_marked_concurrently - bytes_marked_concurrently_;
   1033         bytes_marked_concurrently_ = current_bytes_marked_concurrently;
   1034       }
   1035     }
   1036     if (bytes_marked_ahead_of_schedule_ >= bytes_to_process) {
   1037       // Steps performed in tasks and concurrently have put us ahead of
   1038       // schedule. We skip processing of marking dequeue here and thus shift
   1039       // marking time from inside V8 to standalone tasks.
   1040       bytes_marked_ahead_of_schedule_ -= bytes_to_process;
   1041       bytes_processed += bytes_to_process;
   1042       bytes_to_process = IncrementalMarking::kMinStepSizeInBytes;
   1043     }
   1044     bytes_processed += Step(bytes_to_process, GC_VIA_STACK_GUARD,
   1045                             StepOrigin::kV8, WorklistToProcess::kAll);
   1046     bytes_allocated_ -= Min(bytes_allocated_, bytes_processed);
   1047   }
   1048 }
   1049 
   1050 size_t IncrementalMarking::Step(size_t bytes_to_process,
   1051                                 CompletionAction action, StepOrigin step_origin,
   1052                                 WorklistToProcess worklist_to_process) {
   1053   double start = heap_->MonotonicallyIncreasingTimeInMs();
   1054 
   1055   if (state_ == SWEEPING) {
   1056     TRACE_GC(heap_->tracer(), GCTracer::Scope::MC_INCREMENTAL_SWEEPING);
   1057     FinalizeSweeping();
   1058   }
   1059 
   1060   size_t bytes_processed = 0;
   1061   if (state_ == MARKING) {
   1062     if (FLAG_concurrent_marking) {
   1063       heap_->new_space()->ResetOriginalTop();
   1064       // It is safe to merge back all objects that were on hold to the shared
   1065       // work list at Step because we are at a safepoint where all objects
   1066       // are properly initialized.
   1067       marking_worklist()->shared()->MergeGlobalPool(
   1068           marking_worklist()->on_hold());
   1069     }
   1070 
   1071 // Only print marking worklist in debug mode to save ~40KB of code size.
   1072 #ifdef DEBUG
   1073     if (FLAG_trace_incremental_marking && FLAG_trace_concurrent_marking &&
   1074         FLAG_trace_gc_verbose) {
   1075       marking_worklist()->Print();
   1076     }
   1077 #endif
   1078 
   1079     if (worklist_to_process == WorklistToProcess::kBailout) {
   1080       bytes_processed =
   1081           ProcessMarkingWorklist<WorklistToProcess::kBailout>(bytes_to_process);
   1082     } else {
   1083       bytes_processed =
   1084           ProcessMarkingWorklist<WorklistToProcess::kAll>(bytes_to_process);
   1085     }
   1086 
   1087     if (step_origin == StepOrigin::kTask) {
   1088       bytes_marked_ahead_of_schedule_ += bytes_processed;
   1089     }
   1090 
   1091     if (marking_worklist()->IsEmpty()) {
   1092       if (heap_->local_embedder_heap_tracer()
   1093               ->ShouldFinalizeIncrementalMarking()) {
   1094         if (!finalize_marking_completed_) {
   1095           FinalizeMarking(action);
   1096         } else {
   1097           MarkingComplete(action);
   1098         }
   1099       } else {
   1100         heap_->local_embedder_heap_tracer()->NotifyV8MarkingWorklistWasEmpty();
   1101       }
   1102     }
   1103   }
   1104   if (FLAG_concurrent_marking) {
   1105     heap_->concurrent_marking()->RescheduleTasksIfNeeded();
   1106   }
   1107 
   1108   double end = heap_->MonotonicallyIncreasingTimeInMs();
   1109   double duration = (end - start);
   1110   // Note that we report zero bytes here when sweeping was in progress or
   1111   // when we just started incremental marking. In these cases we did not
   1112   // process the marking deque.
   1113   heap_->tracer()->AddIncrementalMarkingStep(duration, bytes_processed);
   1114   if (FLAG_trace_incremental_marking) {
   1115     heap_->isolate()->PrintWithTimestamp(
   1116         "[IncrementalMarking] Step %s %" PRIuS "KB (%" PRIuS "KB) in %.1f\n",
   1117         step_origin == StepOrigin::kV8 ? "in v8" : "in task",
   1118         bytes_processed / KB, bytes_to_process / KB, duration);
   1119   }
   1120   if (FLAG_trace_concurrent_marking) {
   1121     heap_->isolate()->PrintWithTimestamp(
   1122         "Concurrently marked %" PRIuS "KB\n",
   1123         heap_->concurrent_marking()->TotalMarkedBytes() / KB);
   1124   }
   1125   return bytes_processed;
   1126 }
   1127 
   1128 }  // namespace internal
   1129 }  // namespace v8
   1130