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/v8.h" 6 7 #include "src/incremental-marking.h" 8 9 #include "src/code-stubs.h" 10 #include "src/compilation-cache.h" 11 #include "src/conversions.h" 12 #include "src/objects-visiting.h" 13 #include "src/objects-visiting-inl.h" 14 15 namespace v8 { 16 namespace internal { 17 18 19 IncrementalMarking::IncrementalMarking(Heap* heap) 20 : heap_(heap), 21 state_(STOPPED), 22 marking_deque_memory_(NULL), 23 marking_deque_memory_committed_(false), 24 steps_count_(0), 25 steps_took_(0), 26 longest_step_(0.0), 27 old_generation_space_available_at_start_of_incremental_(0), 28 old_generation_space_used_at_start_of_incremental_(0), 29 steps_count_since_last_gc_(0), 30 steps_took_since_last_gc_(0), 31 should_hurry_(false), 32 marking_speed_(0), 33 allocated_(0), 34 no_marking_scope_depth_(0), 35 unscanned_bytes_of_large_object_(0) { 36 } 37 38 39 void IncrementalMarking::TearDown() { 40 delete marking_deque_memory_; 41 } 42 43 44 void IncrementalMarking::RecordWriteSlow(HeapObject* obj, 45 Object** slot, 46 Object* value) { 47 if (BaseRecordWrite(obj, slot, value) && slot != NULL) { 48 MarkBit obj_bit = Marking::MarkBitFrom(obj); 49 if (Marking::IsBlack(obj_bit)) { 50 // Object is not going to be rescanned we need to record the slot. 51 heap_->mark_compact_collector()->RecordSlot( 52 HeapObject::RawField(obj, 0), slot, value); 53 } 54 } 55 } 56 57 58 void IncrementalMarking::RecordWriteFromCode(HeapObject* obj, 59 Object** slot, 60 Isolate* isolate) { 61 ASSERT(obj->IsHeapObject()); 62 IncrementalMarking* marking = isolate->heap()->incremental_marking(); 63 64 MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address()); 65 int counter = chunk->write_barrier_counter(); 66 if (counter < (MemoryChunk::kWriteBarrierCounterGranularity / 2)) { 67 marking->write_barriers_invoked_since_last_step_ += 68 MemoryChunk::kWriteBarrierCounterGranularity - 69 chunk->write_barrier_counter(); 70 chunk->set_write_barrier_counter( 71 MemoryChunk::kWriteBarrierCounterGranularity); 72 } 73 74 marking->RecordWrite(obj, slot, *slot); 75 } 76 77 78 void IncrementalMarking::RecordCodeTargetPatch(Code* host, 79 Address pc, 80 HeapObject* value) { 81 if (IsMarking()) { 82 RelocInfo rinfo(pc, RelocInfo::CODE_TARGET, 0, host); 83 RecordWriteIntoCode(host, &rinfo, value); 84 } 85 } 86 87 88 void IncrementalMarking::RecordCodeTargetPatch(Address pc, HeapObject* value) { 89 if (IsMarking()) { 90 Code* host = heap_->isolate()->inner_pointer_to_code_cache()-> 91 GcSafeFindCodeForInnerPointer(pc); 92 RelocInfo rinfo(pc, RelocInfo::CODE_TARGET, 0, host); 93 RecordWriteIntoCode(host, &rinfo, value); 94 } 95 } 96 97 98 void IncrementalMarking::RecordWriteOfCodeEntrySlow(JSFunction* host, 99 Object** slot, 100 Code* value) { 101 if (BaseRecordWrite(host, slot, value)) { 102 ASSERT(slot != NULL); 103 heap_->mark_compact_collector()-> 104 RecordCodeEntrySlot(reinterpret_cast<Address>(slot), value); 105 } 106 } 107 108 109 void IncrementalMarking::RecordWriteIntoCodeSlow(HeapObject* obj, 110 RelocInfo* rinfo, 111 Object* value) { 112 MarkBit value_bit = Marking::MarkBitFrom(HeapObject::cast(value)); 113 if (Marking::IsWhite(value_bit)) { 114 MarkBit obj_bit = Marking::MarkBitFrom(obj); 115 if (Marking::IsBlack(obj_bit)) { 116 BlackToGreyAndUnshift(obj, obj_bit); 117 RestartIfNotMarking(); 118 } 119 // Object is either grey or white. It will be scanned if survives. 120 return; 121 } 122 123 if (is_compacting_) { 124 MarkBit obj_bit = Marking::MarkBitFrom(obj); 125 if (Marking::IsBlack(obj_bit)) { 126 // Object is not going to be rescanned. We need to record the slot. 127 heap_->mark_compact_collector()->RecordRelocSlot(rinfo, 128 Code::cast(value)); 129 } 130 } 131 } 132 133 134 static void MarkObjectGreyDoNotEnqueue(Object* obj) { 135 if (obj->IsHeapObject()) { 136 HeapObject* heap_obj = HeapObject::cast(obj); 137 MarkBit mark_bit = Marking::MarkBitFrom(HeapObject::cast(obj)); 138 if (Marking::IsBlack(mark_bit)) { 139 MemoryChunk::IncrementLiveBytesFromGC(heap_obj->address(), 140 -heap_obj->Size()); 141 } 142 Marking::AnyToGrey(mark_bit); 143 } 144 } 145 146 147 static inline void MarkBlackOrKeepGrey(HeapObject* heap_object, 148 MarkBit mark_bit, 149 int size) { 150 ASSERT(!Marking::IsImpossible(mark_bit)); 151 if (mark_bit.Get()) return; 152 mark_bit.Set(); 153 MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(), size); 154 ASSERT(Marking::IsBlack(mark_bit)); 155 } 156 157 158 static inline void MarkBlackOrKeepBlack(HeapObject* heap_object, 159 MarkBit mark_bit, 160 int size) { 161 ASSERT(!Marking::IsImpossible(mark_bit)); 162 if (Marking::IsBlack(mark_bit)) return; 163 Marking::MarkBlack(mark_bit); 164 MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(), size); 165 ASSERT(Marking::IsBlack(mark_bit)); 166 } 167 168 169 class IncrementalMarkingMarkingVisitor 170 : public StaticMarkingVisitor<IncrementalMarkingMarkingVisitor> { 171 public: 172 static void Initialize() { 173 StaticMarkingVisitor<IncrementalMarkingMarkingVisitor>::Initialize(); 174 table_.Register(kVisitFixedArray, &VisitFixedArrayIncremental); 175 table_.Register(kVisitNativeContext, &VisitNativeContextIncremental); 176 table_.Register(kVisitJSRegExp, &VisitJSRegExp); 177 } 178 179 static const int kProgressBarScanningChunk = 32 * 1024; 180 181 static void VisitFixedArrayIncremental(Map* map, HeapObject* object) { 182 MemoryChunk* chunk = MemoryChunk::FromAddress(object->address()); 183 // TODO(mstarzinger): Move setting of the flag to the allocation site of 184 // the array. The visitor should just check the flag. 185 if (FLAG_use_marking_progress_bar && 186 chunk->owner()->identity() == LO_SPACE) { 187 chunk->SetFlag(MemoryChunk::HAS_PROGRESS_BAR); 188 } 189 if (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR)) { 190 Heap* heap = map->GetHeap(); 191 // When using a progress bar for large fixed arrays, scan only a chunk of 192 // the array and try to push it onto the marking deque again until it is 193 // fully scanned. Fall back to scanning it through to the end in case this 194 // fails because of a full deque. 195 int object_size = FixedArray::BodyDescriptor::SizeOf(map, object); 196 int start_offset = Max(FixedArray::BodyDescriptor::kStartOffset, 197 chunk->progress_bar()); 198 int end_offset = Min(object_size, 199 start_offset + kProgressBarScanningChunk); 200 int already_scanned_offset = start_offset; 201 bool scan_until_end = false; 202 do { 203 VisitPointersWithAnchor(heap, 204 HeapObject::RawField(object, 0), 205 HeapObject::RawField(object, start_offset), 206 HeapObject::RawField(object, end_offset)); 207 start_offset = end_offset; 208 end_offset = Min(object_size, end_offset + kProgressBarScanningChunk); 209 scan_until_end = heap->incremental_marking()->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 heap->incremental_marking()->marking_deque()->UnshiftGrey(object); 214 heap->incremental_marking()->NotifyIncompleteScanOfObject( 215 object_size - (start_offset - already_scanned_offset)); 216 } 217 } else { 218 FixedArrayVisitor::Visit(map, object); 219 } 220 } 221 222 static void VisitNativeContextIncremental(Map* map, HeapObject* object) { 223 Context* context = Context::cast(object); 224 225 // We will mark cache black with a separate pass when we finish marking. 226 // Note that GC can happen when the context is not fully initialized, 227 // so the cache can be undefined. 228 Object* cache = context->get(Context::NORMALIZED_MAP_CACHE_INDEX); 229 if (!cache->IsUndefined()) { 230 MarkObjectGreyDoNotEnqueue(cache); 231 } 232 VisitNativeContext(map, context); 233 } 234 235 INLINE(static void VisitPointer(Heap* heap, Object** p)) { 236 Object* obj = *p; 237 if (obj->IsHeapObject()) { 238 heap->mark_compact_collector()->RecordSlot(p, p, obj); 239 MarkObject(heap, obj); 240 } 241 } 242 243 INLINE(static void VisitPointers(Heap* heap, Object** start, Object** end)) { 244 for (Object** p = start; p < end; p++) { 245 Object* obj = *p; 246 if (obj->IsHeapObject()) { 247 heap->mark_compact_collector()->RecordSlot(start, p, obj); 248 MarkObject(heap, obj); 249 } 250 } 251 } 252 253 INLINE(static void VisitPointersWithAnchor(Heap* heap, 254 Object** anchor, 255 Object** start, 256 Object** end)) { 257 for (Object** p = start; p < end; p++) { 258 Object* obj = *p; 259 if (obj->IsHeapObject()) { 260 heap->mark_compact_collector()->RecordSlot(anchor, p, obj); 261 MarkObject(heap, obj); 262 } 263 } 264 } 265 266 // Marks the object grey and pushes it on the marking stack. 267 INLINE(static void MarkObject(Heap* heap, Object* obj)) { 268 HeapObject* heap_object = HeapObject::cast(obj); 269 MarkBit mark_bit = Marking::MarkBitFrom(heap_object); 270 if (mark_bit.data_only()) { 271 MarkBlackOrKeepGrey(heap_object, mark_bit, heap_object->Size()); 272 } else if (Marking::IsWhite(mark_bit)) { 273 heap->incremental_marking()->WhiteToGreyAndPush(heap_object, mark_bit); 274 } 275 } 276 277 // Marks the object black without pushing it on the marking stack. 278 // Returns true if object needed marking and false otherwise. 279 INLINE(static bool MarkObjectWithoutPush(Heap* heap, Object* obj)) { 280 HeapObject* heap_object = HeapObject::cast(obj); 281 MarkBit mark_bit = Marking::MarkBitFrom(heap_object); 282 if (Marking::IsWhite(mark_bit)) { 283 mark_bit.Set(); 284 MemoryChunk::IncrementLiveBytesFromGC(heap_object->address(), 285 heap_object->Size()); 286 return true; 287 } 288 return false; 289 } 290 }; 291 292 293 class IncrementalMarkingRootMarkingVisitor : public ObjectVisitor { 294 public: 295 explicit IncrementalMarkingRootMarkingVisitor( 296 IncrementalMarking* incremental_marking) 297 : incremental_marking_(incremental_marking) { 298 } 299 300 void VisitPointer(Object** p) { 301 MarkObjectByPointer(p); 302 } 303 304 void VisitPointers(Object** start, Object** end) { 305 for (Object** p = start; p < end; p++) MarkObjectByPointer(p); 306 } 307 308 private: 309 void MarkObjectByPointer(Object** p) { 310 Object* obj = *p; 311 if (!obj->IsHeapObject()) return; 312 313 HeapObject* heap_object = HeapObject::cast(obj); 314 MarkBit mark_bit = Marking::MarkBitFrom(heap_object); 315 if (mark_bit.data_only()) { 316 MarkBlackOrKeepGrey(heap_object, mark_bit, heap_object->Size()); 317 } else { 318 if (Marking::IsWhite(mark_bit)) { 319 incremental_marking_->WhiteToGreyAndPush(heap_object, mark_bit); 320 } 321 } 322 } 323 324 IncrementalMarking* incremental_marking_; 325 }; 326 327 328 void IncrementalMarking::Initialize() { 329 IncrementalMarkingMarkingVisitor::Initialize(); 330 } 331 332 333 void IncrementalMarking::SetOldSpacePageFlags(MemoryChunk* chunk, 334 bool is_marking, 335 bool is_compacting) { 336 if (is_marking) { 337 chunk->SetFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); 338 chunk->SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); 339 340 // It's difficult to filter out slots recorded for large objects. 341 if (chunk->owner()->identity() == LO_SPACE && 342 chunk->size() > static_cast<size_t>(Page::kPageSize) && 343 is_compacting) { 344 chunk->SetFlag(MemoryChunk::RESCAN_ON_EVACUATION); 345 } 346 } else if (chunk->owner()->identity() == CELL_SPACE || 347 chunk->owner()->identity() == PROPERTY_CELL_SPACE || 348 chunk->scan_on_scavenge()) { 349 chunk->ClearFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); 350 chunk->ClearFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); 351 } else { 352 chunk->ClearFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); 353 chunk->SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); 354 } 355 } 356 357 358 void IncrementalMarking::SetNewSpacePageFlags(NewSpacePage* chunk, 359 bool is_marking) { 360 chunk->SetFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); 361 if (is_marking) { 362 chunk->SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); 363 } else { 364 chunk->ClearFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); 365 } 366 chunk->SetFlag(MemoryChunk::SCAN_ON_SCAVENGE); 367 } 368 369 370 void IncrementalMarking::DeactivateIncrementalWriteBarrierForSpace( 371 PagedSpace* space) { 372 PageIterator it(space); 373 while (it.has_next()) { 374 Page* p = it.next(); 375 SetOldSpacePageFlags(p, false, false); 376 } 377 } 378 379 380 void IncrementalMarking::DeactivateIncrementalWriteBarrierForSpace( 381 NewSpace* space) { 382 NewSpacePageIterator it(space); 383 while (it.has_next()) { 384 NewSpacePage* p = it.next(); 385 SetNewSpacePageFlags(p, false); 386 } 387 } 388 389 390 void IncrementalMarking::DeactivateIncrementalWriteBarrier() { 391 DeactivateIncrementalWriteBarrierForSpace(heap_->old_pointer_space()); 392 DeactivateIncrementalWriteBarrierForSpace(heap_->old_data_space()); 393 DeactivateIncrementalWriteBarrierForSpace(heap_->cell_space()); 394 DeactivateIncrementalWriteBarrierForSpace(heap_->property_cell_space()); 395 DeactivateIncrementalWriteBarrierForSpace(heap_->map_space()); 396 DeactivateIncrementalWriteBarrierForSpace(heap_->code_space()); 397 DeactivateIncrementalWriteBarrierForSpace(heap_->new_space()); 398 399 LargePage* lop = heap_->lo_space()->first_page(); 400 while (lop->is_valid()) { 401 SetOldSpacePageFlags(lop, false, false); 402 lop = lop->next_page(); 403 } 404 } 405 406 407 void IncrementalMarking::ActivateIncrementalWriteBarrier(PagedSpace* space) { 408 PageIterator it(space); 409 while (it.has_next()) { 410 Page* p = it.next(); 411 SetOldSpacePageFlags(p, true, is_compacting_); 412 } 413 } 414 415 416 void IncrementalMarking::ActivateIncrementalWriteBarrier(NewSpace* space) { 417 NewSpacePageIterator it(space->ToSpaceStart(), space->ToSpaceEnd()); 418 while (it.has_next()) { 419 NewSpacePage* p = it.next(); 420 SetNewSpacePageFlags(p, true); 421 } 422 } 423 424 425 void IncrementalMarking::ActivateIncrementalWriteBarrier() { 426 ActivateIncrementalWriteBarrier(heap_->old_pointer_space()); 427 ActivateIncrementalWriteBarrier(heap_->old_data_space()); 428 ActivateIncrementalWriteBarrier(heap_->cell_space()); 429 ActivateIncrementalWriteBarrier(heap_->property_cell_space()); 430 ActivateIncrementalWriteBarrier(heap_->map_space()); 431 ActivateIncrementalWriteBarrier(heap_->code_space()); 432 ActivateIncrementalWriteBarrier(heap_->new_space()); 433 434 LargePage* lop = heap_->lo_space()->first_page(); 435 while (lop->is_valid()) { 436 SetOldSpacePageFlags(lop, true, is_compacting_); 437 lop = lop->next_page(); 438 } 439 } 440 441 442 bool IncrementalMarking::WorthActivating() { 443 #ifndef DEBUG 444 static const intptr_t kActivationThreshold = 8 * MB; 445 #else 446 // TODO(gc) consider setting this to some low level so that some 447 // debug tests run with incremental marking and some without. 448 static const intptr_t kActivationThreshold = 0; 449 #endif 450 // Only start incremental marking in a safe state: 1) when incremental 451 // marking is turned on, 2) when we are currently not in a GC, and 452 // 3) when we are currently not serializing or deserializing the heap. 453 return FLAG_incremental_marking && 454 FLAG_incremental_marking_steps && 455 heap_->gc_state() == Heap::NOT_IN_GC && 456 !heap_->isolate()->serializer_enabled() && 457 heap_->isolate()->IsInitialized() && 458 heap_->PromotedSpaceSizeOfObjects() > kActivationThreshold; 459 } 460 461 462 void IncrementalMarking::ActivateGeneratedStub(Code* stub) { 463 ASSERT(RecordWriteStub::GetMode(stub) == 464 RecordWriteStub::STORE_BUFFER_ONLY); 465 466 if (!IsMarking()) { 467 // Initially stub is generated in STORE_BUFFER_ONLY mode thus 468 // we don't need to do anything if incremental marking is 469 // not active. 470 } else if (IsCompacting()) { 471 RecordWriteStub::Patch(stub, RecordWriteStub::INCREMENTAL_COMPACTION); 472 } else { 473 RecordWriteStub::Patch(stub, RecordWriteStub::INCREMENTAL); 474 } 475 } 476 477 478 static void PatchIncrementalMarkingRecordWriteStubs( 479 Heap* heap, RecordWriteStub::Mode mode) { 480 UnseededNumberDictionary* stubs = heap->code_stubs(); 481 482 int capacity = stubs->Capacity(); 483 for (int i = 0; i < capacity; i++) { 484 Object* k = stubs->KeyAt(i); 485 if (stubs->IsKey(k)) { 486 uint32_t key = NumberToUint32(k); 487 488 if (CodeStub::MajorKeyFromKey(key) == 489 CodeStub::RecordWrite) { 490 Object* e = stubs->ValueAt(i); 491 if (e->IsCode()) { 492 RecordWriteStub::Patch(Code::cast(e), mode); 493 } 494 } 495 } 496 } 497 } 498 499 500 void IncrementalMarking::EnsureMarkingDequeIsCommitted() { 501 if (marking_deque_memory_ == NULL) { 502 marking_deque_memory_ = new VirtualMemory(4 * MB); 503 } 504 if (!marking_deque_memory_committed_) { 505 bool success = marking_deque_memory_->Commit( 506 reinterpret_cast<Address>(marking_deque_memory_->address()), 507 marking_deque_memory_->size(), 508 false); // Not executable. 509 CHECK(success); 510 marking_deque_memory_committed_ = true; 511 } 512 } 513 514 515 void IncrementalMarking::UncommitMarkingDeque() { 516 if (state_ == STOPPED && marking_deque_memory_committed_) { 517 bool success = marking_deque_memory_->Uncommit( 518 reinterpret_cast<Address>(marking_deque_memory_->address()), 519 marking_deque_memory_->size()); 520 CHECK(success); 521 marking_deque_memory_committed_ = false; 522 } 523 } 524 525 526 void IncrementalMarking::Start(CompactionFlag flag) { 527 if (FLAG_trace_incremental_marking) { 528 PrintF("[IncrementalMarking] Start\n"); 529 } 530 ASSERT(FLAG_incremental_marking); 531 ASSERT(FLAG_incremental_marking_steps); 532 ASSERT(state_ == STOPPED); 533 ASSERT(heap_->gc_state() == Heap::NOT_IN_GC); 534 ASSERT(!heap_->isolate()->serializer_enabled()); 535 ASSERT(heap_->isolate()->IsInitialized()); 536 537 ResetStepCounters(); 538 539 if (!heap_->mark_compact_collector()->IsConcurrentSweepingInProgress()) { 540 StartMarking(flag); 541 } else { 542 if (FLAG_trace_incremental_marking) { 543 PrintF("[IncrementalMarking] Start sweeping.\n"); 544 } 545 state_ = SWEEPING; 546 } 547 548 heap_->new_space()->LowerInlineAllocationLimit(kAllocatedThreshold); 549 } 550 551 552 void IncrementalMarking::StartMarking(CompactionFlag flag) { 553 if (FLAG_trace_incremental_marking) { 554 PrintF("[IncrementalMarking] Start marking\n"); 555 } 556 557 is_compacting_ = !FLAG_never_compact && (flag == ALLOW_COMPACTION) && 558 heap_->mark_compact_collector()->StartCompaction( 559 MarkCompactCollector::INCREMENTAL_COMPACTION); 560 561 state_ = MARKING; 562 563 RecordWriteStub::Mode mode = is_compacting_ ? 564 RecordWriteStub::INCREMENTAL_COMPACTION : RecordWriteStub::INCREMENTAL; 565 566 PatchIncrementalMarkingRecordWriteStubs(heap_, mode); 567 568 EnsureMarkingDequeIsCommitted(); 569 570 // Initialize marking stack. 571 Address addr = static_cast<Address>(marking_deque_memory_->address()); 572 size_t size = marking_deque_memory_->size(); 573 if (FLAG_force_marking_deque_overflows) size = 64 * kPointerSize; 574 marking_deque_.Initialize(addr, addr + size); 575 576 ActivateIncrementalWriteBarrier(); 577 578 // Marking bits are cleared by the sweeper. 579 #ifdef VERIFY_HEAP 580 if (FLAG_verify_heap) { 581 heap_->mark_compact_collector()->VerifyMarkbitsAreClean(); 582 } 583 #endif 584 585 heap_->CompletelyClearInstanceofCache(); 586 heap_->isolate()->compilation_cache()->MarkCompactPrologue(); 587 588 if (FLAG_cleanup_code_caches_at_gc) { 589 // We will mark cache black with a separate pass 590 // when we finish marking. 591 MarkObjectGreyDoNotEnqueue(heap_->polymorphic_code_cache()); 592 } 593 594 // Mark strong roots grey. 595 IncrementalMarkingRootMarkingVisitor visitor(this); 596 heap_->IterateStrongRoots(&visitor, VISIT_ONLY_STRONG); 597 598 heap_->mark_compact_collector()->MarkWeakObjectToCodeTable(); 599 600 // Ready to start incremental marking. 601 if (FLAG_trace_incremental_marking) { 602 PrintF("[IncrementalMarking] Running\n"); 603 } 604 } 605 606 607 void IncrementalMarking::PrepareForScavenge() { 608 if (!IsMarking()) return; 609 NewSpacePageIterator it(heap_->new_space()->FromSpaceStart(), 610 heap_->new_space()->FromSpaceEnd()); 611 while (it.has_next()) { 612 Bitmap::Clear(it.next()); 613 } 614 } 615 616 617 void IncrementalMarking::UpdateMarkingDequeAfterScavenge() { 618 if (!IsMarking()) return; 619 620 int current = marking_deque_.bottom(); 621 int mask = marking_deque_.mask(); 622 int limit = marking_deque_.top(); 623 HeapObject** array = marking_deque_.array(); 624 int new_top = current; 625 626 Map* filler_map = heap_->one_pointer_filler_map(); 627 628 while (current != limit) { 629 HeapObject* obj = array[current]; 630 ASSERT(obj->IsHeapObject()); 631 current = ((current + 1) & mask); 632 if (heap_->InNewSpace(obj)) { 633 MapWord map_word = obj->map_word(); 634 if (map_word.IsForwardingAddress()) { 635 HeapObject* dest = map_word.ToForwardingAddress(); 636 array[new_top] = dest; 637 new_top = ((new_top + 1) & mask); 638 ASSERT(new_top != marking_deque_.bottom()); 639 #ifdef DEBUG 640 MarkBit mark_bit = Marking::MarkBitFrom(obj); 641 ASSERT(Marking::IsGrey(mark_bit) || 642 (obj->IsFiller() && Marking::IsWhite(mark_bit))); 643 #endif 644 } 645 } else if (obj->map() != filler_map) { 646 // Skip one word filler objects that appear on the 647 // stack when we perform in place array shift. 648 array[new_top] = obj; 649 new_top = ((new_top + 1) & mask); 650 ASSERT(new_top != marking_deque_.bottom()); 651 #ifdef DEBUG 652 MarkBit mark_bit = Marking::MarkBitFrom(obj); 653 MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address()); 654 ASSERT(Marking::IsGrey(mark_bit) || 655 (obj->IsFiller() && Marking::IsWhite(mark_bit)) || 656 (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR) && 657 Marking::IsBlack(mark_bit))); 658 #endif 659 } 660 } 661 marking_deque_.set_top(new_top); 662 663 steps_took_since_last_gc_ = 0; 664 steps_count_since_last_gc_ = 0; 665 longest_step_ = 0.0; 666 } 667 668 669 void IncrementalMarking::VisitObject(Map* map, HeapObject* obj, int size) { 670 MarkBit map_mark_bit = Marking::MarkBitFrom(map); 671 if (Marking::IsWhite(map_mark_bit)) { 672 WhiteToGreyAndPush(map, map_mark_bit); 673 } 674 675 IncrementalMarkingMarkingVisitor::IterateBody(map, obj); 676 677 MarkBit mark_bit = Marking::MarkBitFrom(obj); 678 #if ENABLE_SLOW_ASSERTS 679 MemoryChunk* chunk = MemoryChunk::FromAddress(obj->address()); 680 SLOW_ASSERT(Marking::IsGrey(mark_bit) || 681 (obj->IsFiller() && Marking::IsWhite(mark_bit)) || 682 (chunk->IsFlagSet(MemoryChunk::HAS_PROGRESS_BAR) && 683 Marking::IsBlack(mark_bit))); 684 #endif 685 MarkBlackOrKeepBlack(obj, mark_bit, size); 686 } 687 688 689 void IncrementalMarking::ProcessMarkingDeque(intptr_t bytes_to_process) { 690 Map* filler_map = heap_->one_pointer_filler_map(); 691 while (!marking_deque_.IsEmpty() && bytes_to_process > 0) { 692 HeapObject* obj = marking_deque_.Pop(); 693 694 // Explicitly skip one word fillers. Incremental markbit patterns are 695 // correct only for objects that occupy at least two words. 696 Map* map = obj->map(); 697 if (map == filler_map) continue; 698 699 int size = obj->SizeFromMap(map); 700 unscanned_bytes_of_large_object_ = 0; 701 VisitObject(map, obj, size); 702 bytes_to_process -= (size - unscanned_bytes_of_large_object_); 703 } 704 } 705 706 707 void IncrementalMarking::ProcessMarkingDeque() { 708 Map* filler_map = heap_->one_pointer_filler_map(); 709 while (!marking_deque_.IsEmpty()) { 710 HeapObject* obj = marking_deque_.Pop(); 711 712 // Explicitly skip one word fillers. Incremental markbit patterns are 713 // correct only for objects that occupy at least two words. 714 Map* map = obj->map(); 715 if (map == filler_map) continue; 716 717 VisitObject(map, obj, obj->SizeFromMap(map)); 718 } 719 } 720 721 722 void IncrementalMarking::Hurry() { 723 if (state() == MARKING) { 724 double start = 0.0; 725 if (FLAG_trace_incremental_marking || FLAG_print_cumulative_gc_stat) { 726 start = OS::TimeCurrentMillis(); 727 if (FLAG_trace_incremental_marking) { 728 PrintF("[IncrementalMarking] Hurry\n"); 729 } 730 } 731 // TODO(gc) hurry can mark objects it encounters black as mutator 732 // was stopped. 733 ProcessMarkingDeque(); 734 state_ = COMPLETE; 735 if (FLAG_trace_incremental_marking || FLAG_print_cumulative_gc_stat) { 736 double end = OS::TimeCurrentMillis(); 737 double delta = end - start; 738 heap_->AddMarkingTime(delta); 739 if (FLAG_trace_incremental_marking) { 740 PrintF("[IncrementalMarking] Complete (hurry), spent %d ms.\n", 741 static_cast<int>(delta)); 742 } 743 } 744 } 745 746 if (FLAG_cleanup_code_caches_at_gc) { 747 PolymorphicCodeCache* poly_cache = heap_->polymorphic_code_cache(); 748 Marking::GreyToBlack(Marking::MarkBitFrom(poly_cache)); 749 MemoryChunk::IncrementLiveBytesFromGC(poly_cache->address(), 750 PolymorphicCodeCache::kSize); 751 } 752 753 Object* context = heap_->native_contexts_list(); 754 while (!context->IsUndefined()) { 755 // GC can happen when the context is not fully initialized, 756 // so the cache can be undefined. 757 HeapObject* cache = HeapObject::cast( 758 Context::cast(context)->get(Context::NORMALIZED_MAP_CACHE_INDEX)); 759 if (!cache->IsUndefined()) { 760 MarkBit mark_bit = Marking::MarkBitFrom(cache); 761 if (Marking::IsGrey(mark_bit)) { 762 Marking::GreyToBlack(mark_bit); 763 MemoryChunk::IncrementLiveBytesFromGC(cache->address(), cache->Size()); 764 } 765 } 766 context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); 767 } 768 } 769 770 771 void IncrementalMarking::Abort() { 772 if (IsStopped()) return; 773 if (FLAG_trace_incremental_marking) { 774 PrintF("[IncrementalMarking] Aborting.\n"); 775 } 776 heap_->new_space()->LowerInlineAllocationLimit(0); 777 IncrementalMarking::set_should_hurry(false); 778 ResetStepCounters(); 779 if (IsMarking()) { 780 PatchIncrementalMarkingRecordWriteStubs(heap_, 781 RecordWriteStub::STORE_BUFFER_ONLY); 782 DeactivateIncrementalWriteBarrier(); 783 784 if (is_compacting_) { 785 LargeObjectIterator it(heap_->lo_space()); 786 for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { 787 Page* p = Page::FromAddress(obj->address()); 788 if (p->IsFlagSet(Page::RESCAN_ON_EVACUATION)) { 789 p->ClearFlag(Page::RESCAN_ON_EVACUATION); 790 } 791 } 792 } 793 } 794 heap_->isolate()->stack_guard()->ClearGC(); 795 state_ = STOPPED; 796 is_compacting_ = false; 797 } 798 799 800 void IncrementalMarking::Finalize() { 801 Hurry(); 802 state_ = STOPPED; 803 is_compacting_ = false; 804 heap_->new_space()->LowerInlineAllocationLimit(0); 805 IncrementalMarking::set_should_hurry(false); 806 ResetStepCounters(); 807 PatchIncrementalMarkingRecordWriteStubs(heap_, 808 RecordWriteStub::STORE_BUFFER_ONLY); 809 DeactivateIncrementalWriteBarrier(); 810 ASSERT(marking_deque_.IsEmpty()); 811 heap_->isolate()->stack_guard()->ClearGC(); 812 } 813 814 815 void IncrementalMarking::MarkingComplete(CompletionAction action) { 816 state_ = COMPLETE; 817 // We will set the stack guard to request a GC now. This will mean the rest 818 // of the GC gets performed as soon as possible (we can't do a GC here in a 819 // record-write context). If a few things get allocated between now and then 820 // that shouldn't make us do a scavenge and keep being incremental, so we set 821 // the should-hurry flag to indicate that there can't be much work left to do. 822 set_should_hurry(true); 823 if (FLAG_trace_incremental_marking) { 824 PrintF("[IncrementalMarking] Complete (normal).\n"); 825 } 826 if (action == GC_VIA_STACK_GUARD) { 827 heap_->isolate()->stack_guard()->RequestGC(); 828 } 829 } 830 831 832 void IncrementalMarking::OldSpaceStep(intptr_t allocated) { 833 if (IsStopped() && WorthActivating() && heap_->NextGCIsLikelyToBeFull()) { 834 // TODO(hpayer): Let's play safe for now, but compaction should be 835 // in principle possible. 836 Start(PREVENT_COMPACTION); 837 } else { 838 Step(allocated * kFastMarking / kInitialMarkingSpeed, GC_VIA_STACK_GUARD); 839 } 840 } 841 842 843 void IncrementalMarking::Step(intptr_t allocated_bytes, 844 CompletionAction action) { 845 if (heap_->gc_state() != Heap::NOT_IN_GC || 846 !FLAG_incremental_marking || 847 !FLAG_incremental_marking_steps || 848 (state_ != SWEEPING && state_ != MARKING)) { 849 return; 850 } 851 852 allocated_ += allocated_bytes; 853 854 if (allocated_ < kAllocatedThreshold && 855 write_barriers_invoked_since_last_step_ < 856 kWriteBarriersInvokedThreshold) { 857 return; 858 } 859 860 if (state_ == MARKING && no_marking_scope_depth_ > 0) return; 861 862 // The marking speed is driven either by the allocation rate or by the rate 863 // at which we are having to check the color of objects in the write barrier. 864 // It is possible for a tight non-allocating loop to run a lot of write 865 // barriers before we get here and check them (marking can only take place on 866 // allocation), so to reduce the lumpiness we don't use the write barriers 867 // invoked since last step directly to determine the amount of work to do. 868 intptr_t bytes_to_process = 869 marking_speed_ * Max(allocated_, write_barriers_invoked_since_last_step_); 870 allocated_ = 0; 871 write_barriers_invoked_since_last_step_ = 0; 872 873 bytes_scanned_ += bytes_to_process; 874 875 double start = 0; 876 877 if (FLAG_trace_incremental_marking || FLAG_trace_gc || 878 FLAG_print_cumulative_gc_stat) { 879 start = OS::TimeCurrentMillis(); 880 } 881 882 if (state_ == SWEEPING) { 883 if (heap_->mark_compact_collector()->IsConcurrentSweepingInProgress() && 884 heap_->mark_compact_collector()->IsSweepingCompleted()) { 885 heap_->mark_compact_collector()->WaitUntilSweepingCompleted(); 886 } 887 if (!heap_->mark_compact_collector()->IsConcurrentSweepingInProgress()) { 888 bytes_scanned_ = 0; 889 StartMarking(PREVENT_COMPACTION); 890 } 891 } else if (state_ == MARKING) { 892 ProcessMarkingDeque(bytes_to_process); 893 if (marking_deque_.IsEmpty()) MarkingComplete(action); 894 } 895 896 steps_count_++; 897 steps_count_since_last_gc_++; 898 899 bool speed_up = false; 900 901 if ((steps_count_ % kMarkingSpeedAccellerationInterval) == 0) { 902 if (FLAG_trace_gc) { 903 PrintPID("Speed up marking after %d steps\n", 904 static_cast<int>(kMarkingSpeedAccellerationInterval)); 905 } 906 speed_up = true; 907 } 908 909 bool space_left_is_very_small = 910 (old_generation_space_available_at_start_of_incremental_ < 10 * MB); 911 912 bool only_1_nth_of_space_that_was_available_still_left = 913 (SpaceLeftInOldSpace() * (marking_speed_ + 1) < 914 old_generation_space_available_at_start_of_incremental_); 915 916 if (space_left_is_very_small || 917 only_1_nth_of_space_that_was_available_still_left) { 918 if (FLAG_trace_gc) PrintPID("Speed up marking because of low space left\n"); 919 speed_up = true; 920 } 921 922 bool size_of_old_space_multiplied_by_n_during_marking = 923 (heap_->PromotedTotalSize() > 924 (marking_speed_ + 1) * 925 old_generation_space_used_at_start_of_incremental_); 926 if (size_of_old_space_multiplied_by_n_during_marking) { 927 speed_up = true; 928 if (FLAG_trace_gc) { 929 PrintPID("Speed up marking because of heap size increase\n"); 930 } 931 } 932 933 int64_t promoted_during_marking = heap_->PromotedTotalSize() 934 - old_generation_space_used_at_start_of_incremental_; 935 intptr_t delay = marking_speed_ * MB; 936 intptr_t scavenge_slack = heap_->MaxSemiSpaceSize(); 937 938 // We try to scan at at least twice the speed that we are allocating. 939 if (promoted_during_marking > bytes_scanned_ / 2 + scavenge_slack + delay) { 940 if (FLAG_trace_gc) { 941 PrintPID("Speed up marking because marker was not keeping up\n"); 942 } 943 speed_up = true; 944 } 945 946 if (speed_up) { 947 if (state_ != MARKING) { 948 if (FLAG_trace_gc) { 949 PrintPID("Postponing speeding up marking until marking starts\n"); 950 } 951 } else { 952 marking_speed_ += kMarkingSpeedAccelleration; 953 marking_speed_ = static_cast<int>( 954 Min(kMaxMarkingSpeed, 955 static_cast<intptr_t>(marking_speed_ * 1.3))); 956 if (FLAG_trace_gc) { 957 PrintPID("Marking speed increased to %d\n", marking_speed_); 958 } 959 } 960 } 961 962 if (FLAG_trace_incremental_marking || FLAG_trace_gc || 963 FLAG_print_cumulative_gc_stat) { 964 double end = OS::TimeCurrentMillis(); 965 double delta = (end - start); 966 longest_step_ = Max(longest_step_, delta); 967 steps_took_ += delta; 968 steps_took_since_last_gc_ += delta; 969 heap_->AddMarkingTime(delta); 970 } 971 } 972 973 974 void IncrementalMarking::ResetStepCounters() { 975 steps_count_ = 0; 976 steps_took_ = 0; 977 longest_step_ = 0.0; 978 old_generation_space_available_at_start_of_incremental_ = 979 SpaceLeftInOldSpace(); 980 old_generation_space_used_at_start_of_incremental_ = 981 heap_->PromotedTotalSize(); 982 steps_count_since_last_gc_ = 0; 983 steps_took_since_last_gc_ = 0; 984 bytes_rescanned_ = 0; 985 marking_speed_ = kInitialMarkingSpeed; 986 bytes_scanned_ = 0; 987 write_barriers_invoked_since_last_step_ = 0; 988 } 989 990 991 int64_t IncrementalMarking::SpaceLeftInOldSpace() { 992 return heap_->MaxOldGenerationSize() - heap_->PromotedSpaceSizeOfObjects(); 993 } 994 995 } } // namespace v8::internal 996