Chromium Code Reviews| Index: src/heap/mark-compact.cc |
| diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc |
| index ab6907ecc674a40c653c93741676b004e7157ff6..bf624b04cf57beb4d6346259bc88fad08f262e8c 100644 |
| --- a/src/heap/mark-compact.cc |
| +++ b/src/heap/mark-compact.cc |
| @@ -447,6 +447,8 @@ void MarkCompactCollector::CollectGarbage() { |
| // update the state as they proceed. |
| DCHECK(state_ == PREPARE_GC); |
| + heap()->minor_mark_compact_collector()->CleanupSweepToIteratePages(); |
| + |
| MarkLiveObjects(); |
| DCHECK(heap_->incremental_marking()->IsStopped()); |
| @@ -1458,6 +1460,16 @@ void MarkCompactCollector::PrepareForCodeFlushing() { |
| ProcessMarkingDeque(); |
| } |
| +void MinorMarkCompactCollector::CleanupSweepToIteratePages() { |
| + for (Page* p : sweep_to_iterate_pages_) { |
| + if (p->IsFlagSet(Page::SWEEP_TO_ITERATE)) { |
| + p->ClearFlag(Page::SWEEP_TO_ITERATE); |
| + marking_state(p).ClearLiveness(); |
| + } |
| + } |
| + sweep_to_iterate_pages_.clear(); |
| +} |
| + |
| class MinorMarkCompactCollector::RootMarkingVisitor : public RootVisitor { |
| public: |
| explicit RootMarkingVisitor(MinorMarkCompactCollector* collector) |
| @@ -2588,6 +2600,8 @@ void MinorMarkCompactCollector::EmptyMarkingDeque() { |
| void MinorMarkCompactCollector::CollectGarbage() { |
| heap()->mark_compact_collector()->sweeper().EnsureNewSpaceCompleted(); |
| + CleanupSweepToIteratePages(); |
| + |
| MarkLiveObjects(); |
| ClearNonLiveReferences(); |
| #ifdef VERIFY_HEAP |
| @@ -2611,13 +2625,65 @@ void MinorMarkCompactCollector::CollectGarbage() { |
| TRACE_GC(heap()->tracer(), GCTracer::Scope::MINOR_MC_CLEAR_LIVENESS); |
| for (Page* p : PageRange(heap()->new_space()->FromSpaceStart(), |
| heap()->new_space()->FromSpaceEnd())) { |
| - marking_state(p).ClearLiveness(); |
| + if (!p->IsFlagSet(Page::SWEEP_TO_ITERATE)) { |
| + marking_state(p).ClearLiveness(); |
| + } |
| } |
| } |
| heap()->account_external_memory_concurrently_freed(); |
| } |
| +void MinorMarkCompactCollector::MakeIterable( |
| + Page* p, MarkingTreatmentMode marking_mode, |
| + FreeSpaceTreatmentMode free_space_mode) { |
| + // We have to clear the full collectors markbits for the areas that we |
| + // remove here. |
| + MarkCompactCollector* full_collector = heap()->mark_compact_collector(); |
| + Address free_start = p->area_start(); |
| + DCHECK(reinterpret_cast<intptr_t>(free_start) % (32 * kPointerSize) == 0); |
| + LiveObjectIterator<kBlackObjects> it(p, marking_state(p)); |
| + HeapObject* object = nullptr; |
| + |
| + while ((object = it.Next()) != nullptr) { |
| + DCHECK(ObjectMarking::IsBlack(object, marking_state(object))); |
| + Address free_end = object->address(); |
| + if (free_end != free_start) { |
| + CHECK_GT(free_end, free_start); |
| + size_t size = static_cast<size_t>(free_end - free_start); |
| + if (free_space_mode == ZAP_FREE_SPACE) { |
| + memset(free_start, 0xcc, size); |
| + } |
| + full_collector->marking_state(p).bitmap()->ClearRange( |
|
ulan
2017/05/09 11:31:54
Shouldn't this be guarded with marking_mode?
Michael Lippautz
2017/05/09 13:01:25
Done.
|
| + p->AddressToMarkbitIndex(free_start), |
| + p->AddressToMarkbitIndex(free_end)); |
| + p->heap()->CreateFillerObjectAt(free_start, static_cast<int>(size), |
| + ClearRecordedSlots::kNo); |
| + } |
| + Map* map = object->synchronized_map(); |
| + int size = object->SizeFromMap(map); |
| + free_start = free_end + size; |
| + } |
| + |
| + if (free_start != p->area_end()) { |
| + CHECK_GT(p->area_end(), free_start); |
| + size_t size = static_cast<size_t>(p->area_end() - free_start); |
| + if (free_space_mode == ZAP_FREE_SPACE) { |
| + memset(free_start, 0xcc, size); |
| + } |
| + full_collector->marking_state(p).bitmap()->ClearRange( |
| + p->AddressToMarkbitIndex(free_start), |
| + p->AddressToMarkbitIndex(p->area_end())); |
| + p->heap()->CreateFillerObjectAt(free_start, static_cast<int>(size), |
| + ClearRecordedSlots::kNo); |
| + } |
| + |
| + if (marking_mode == MarkingTreatmentMode::CLEAR) { |
| + marking_state(p).ClearLiveness(); |
| + p->ClearFlag(Page::SWEEP_TO_ITERATE); |
| + } |
| +} |
| + |
| void MinorMarkCompactCollector::ClearNonLiveReferences() { |
| TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_CLEAR); |
| @@ -2680,7 +2746,15 @@ void MinorMarkCompactCollector::Evacuate() { |
| { |
| TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_EVACUATE_CLEAN_UP); |
| - // TODO(mlippautz): Implement page promotion. |
| + for (Page* p : new_space_evacuation_pages_) { |
| + if (p->IsFlagSet(Page::PAGE_NEW_NEW_PROMOTION) || |
| + p->IsFlagSet(Page::PAGE_NEW_OLD_PROMOTION)) { |
| + p->ClearFlag(Page::PAGE_NEW_NEW_PROMOTION); |
| + p->ClearFlag(Page::PAGE_NEW_OLD_PROMOTION); |
| + p->SetFlag(Page::SWEEP_TO_ITERATE); |
| + sweep_to_iterate_pages_.push_back(p); |
| + } |
| + } |
| new_space_evacuation_pages_.Rewind(0); |
| } |
| @@ -3571,12 +3645,30 @@ bool YoungGenerationEvacuator::RawEvacuatePage(Page* page, |
| page, ArrayBufferTracker::kUpdateForwardedRemoveOthers); |
| break; |
| case kPageNewToOld: |
| - // TODO(mlippautz): Implement page promotion. |
| - UNREACHABLE(); |
| + success = object_visitor.VisitBlackObjects( |
| + page, state, &new_to_old_page_visitor_, |
| + LiveObjectVisitor::kKeepMarking); |
| + DCHECK(success); |
| + new_to_old_page_visitor_.account_moved_bytes(state.live_bytes()); |
| + // TODO(mlippautz): If cleaning array buffers is too slow here we can |
| + // delay it until the next GC. |
| + ArrayBufferTracker::FreeDead(page, state); |
| + if (heap()->ShouldZapGarbage()) |
| + collector_->MakeIterable(page, MarkingTreatmentMode::KEEP, |
| + ZAP_FREE_SPACE); |
| break; |
| case kPageNewToNew: |
| - // TODO(mlippautz): Implement page promotion. |
| - UNREACHABLE(); |
| + success = object_visitor.VisitBlackObjects( |
| + page, state, &new_to_new_page_visitor_, |
| + LiveObjectVisitor::kKeepMarking); |
| + DCHECK(success); |
| + new_to_new_page_visitor_.account_moved_bytes(state.live_bytes()); |
| + // TODO(mlippautz): If cleaning array buffers is too slow here we can |
| + // delay it until the next GC. |
| + ArrayBufferTracker::FreeDead(page, state); |
| + if (heap()->ShouldZapGarbage()) |
| + collector_->MakeIterable(page, MarkingTreatmentMode::KEEP, |
| + ZAP_FREE_SPACE); |
| break; |
| case kObjectsOldToOld: |
| UNREACHABLE(); |
| @@ -3716,7 +3808,6 @@ void MarkCompactCollector::EvacuatePagesInParallel() { |
| EvacuateNewSpacePageVisitor<NEW_TO_NEW>::Move(page); |
| } |
| } |
| - |
| job.AddPage(page, {&abandoned_pages, marking_state(page)}); |
| } |
| DCHECK_GE(job.NumberOfPages(), 1); |
| @@ -3733,10 +3824,21 @@ void MinorMarkCompactCollector::EvacuatePagesInParallel() { |
| int abandoned_pages = 0; |
| intptr_t live_bytes = 0; |
| + const bool reduce_memory = heap()->ShouldReduceMemory(); |
| + const Address age_mark = heap()->new_space()->age_mark(); |
| for (Page* page : new_space_evacuation_pages_) { |
| intptr_t live_bytes_on_page = marking_state(page).live_bytes(); |
| live_bytes += live_bytes_on_page; |
| - // TODO(mlippautz): Implement page promotion. |
| + if (!reduce_memory && !page->NeverEvacuate() && |
|
ulan
2017/05/09 11:31:53
Let's extract this predicate into a separate funct
Michael Lippautz
2017/05/09 13:01:24
Done.
|
| + (live_bytes_on_page > Evacuator::PageEvacuationThreshold()) && |
| + !page->Contains(age_mark) && |
| + heap()->CanExpandOldGeneration(live_bytes_on_page)) { |
| + if (page->IsFlagSet(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK)) { |
| + EvacuateNewSpacePageVisitor<NEW_TO_OLD>::Move(page); |
| + } else { |
| + EvacuateNewSpacePageVisitor<NEW_TO_NEW>::Move(page); |
| + } |
| + } |
| job.AddPage(page, {&abandoned_pages, marking_state(page)}); |
| } |
| DCHECK_GE(job.NumberOfPages(), 1); |
| @@ -4060,12 +4162,12 @@ template <RememberedSetType type> |
| class PointerUpdateJobTraits { |
| public: |
| typedef int PerPageData; // Per page data is not used in this job. |
| - typedef int PerTaskData; // Per task data is not used in this job. |
| + typedef const MarkCompactCollectorBase* PerTaskData; |
| - static bool ProcessPageInParallel(Heap* heap, PerTaskData, MemoryChunk* chunk, |
| - PerPageData) { |
| - UpdateUntypedPointers(heap, chunk); |
| - UpdateTypedPointers(heap, chunk); |
| + static bool ProcessPageInParallel(Heap* heap, PerTaskData task_data, |
| + MemoryChunk* chunk, PerPageData) { |
| + UpdateUntypedPointers(heap, chunk, task_data); |
| + UpdateTypedPointers(heap, chunk, task_data); |
| return true; |
| } |
| static const bool NeedSequentialFinalization = false; |
| @@ -4073,12 +4175,14 @@ class PointerUpdateJobTraits { |
| } |
| private: |
| - static void UpdateUntypedPointers(Heap* heap, MemoryChunk* chunk) { |
| + static void UpdateUntypedPointers(Heap* heap, MemoryChunk* chunk, |
| + const MarkCompactCollectorBase* collector) { |
| base::LockGuard<base::RecursiveMutex> guard(chunk->mutex()); |
| if (type == OLD_TO_NEW) { |
| - RememberedSet<OLD_TO_NEW>::Iterate(chunk, [heap](Address slot) { |
| - return CheckAndUpdateOldToNewSlot(heap, slot); |
| - }); |
| + RememberedSet<OLD_TO_NEW>::Iterate( |
| + chunk, [heap, collector](Address slot) { |
| + return CheckAndUpdateOldToNewSlot(heap, slot, collector); |
| + }); |
| } else { |
| RememberedSet<OLD_TO_OLD>::Iterate(chunk, [](Address slot) { |
| return UpdateSlot(reinterpret_cast<Object**>(slot)); |
| @@ -4086,7 +4190,8 @@ class PointerUpdateJobTraits { |
| } |
| } |
| - static void UpdateTypedPointers(Heap* heap, MemoryChunk* chunk) { |
| + static void UpdateTypedPointers(Heap* heap, MemoryChunk* chunk, |
| + const MarkCompactCollectorBase* collector) { |
| if (type == OLD_TO_OLD) { |
| Isolate* isolate = heap->isolate(); |
| RememberedSet<OLD_TO_OLD>::IterateTyped( |
| @@ -4098,19 +4203,20 @@ class PointerUpdateJobTraits { |
| } else { |
| Isolate* isolate = heap->isolate(); |
| RememberedSet<OLD_TO_NEW>::IterateTyped( |
| - chunk, |
| - [isolate, heap](SlotType slot_type, Address host_addr, Address slot) { |
| + chunk, [isolate, heap, collector](SlotType slot_type, |
| + Address host_addr, Address slot) { |
| return UpdateTypedSlotHelper::UpdateTypedSlot( |
| - isolate, slot_type, slot, [heap](Object** slot) { |
| + isolate, slot_type, slot, [heap, collector](Object** slot) { |
| return CheckAndUpdateOldToNewSlot( |
| - heap, reinterpret_cast<Address>(slot)); |
| + heap, reinterpret_cast<Address>(slot), collector); |
| }); |
| }); |
| } |
| } |
| - static SlotCallbackResult CheckAndUpdateOldToNewSlot(Heap* heap, |
| - Address slot_address) { |
| + static SlotCallbackResult CheckAndUpdateOldToNewSlot( |
| + Heap* heap, Address slot_address, |
| + const MarkCompactCollectorBase* collector) { |
| // There may be concurrent action on slots in dead objects. Concurrent |
| // sweeper threads may overwrite the slot content with a free space object. |
| // Moreover, the pointed-to object may also get concurrently overwritten |
| @@ -4150,7 +4256,7 @@ class PointerUpdateJobTraits { |
| // markbits to determine liveness. |
| HeapObject* heap_object = reinterpret_cast<HeapObject*>(slot_reference); |
| if (ObjectMarking::IsBlack(heap_object, |
| - MarkingState::Internal(heap_object))) |
| + collector->marking_state(heap_object))) |
| return KEEP_SLOT; |
| } else { |
| DCHECK(!heap->InNewSpace(slot_reference)); |
| @@ -4169,14 +4275,15 @@ int NumberOfPointerUpdateTasks(int pages) { |
| } |
| template <RememberedSetType type> |
| -void UpdatePointersInParallel(Heap* heap, base::Semaphore* semaphore) { |
| +void UpdatePointersInParallel(Heap* heap, base::Semaphore* semaphore, |
| + const MarkCompactCollectorBase* collector) { |
| PageParallelJob<PointerUpdateJobTraits<type> > job( |
| heap, heap->isolate()->cancelable_task_manager(), semaphore); |
| RememberedSet<type>::IterateMemoryChunks( |
| heap, [&job](MemoryChunk* chunk) { job.AddPage(chunk, 0); }); |
| int num_pages = job.NumberOfPages(); |
| int num_tasks = NumberOfPointerUpdateTasks(num_pages); |
| - job.Run(num_tasks, [](int i) { return 0; }); |
| + job.Run(num_tasks, [collector](int i) { return collector; }); |
| } |
| class ToSpacePointerUpdateJobTraits { |
| @@ -4263,14 +4370,16 @@ void MarkCompactCollector::UpdatePointersAfterEvacuation() { |
| // Update roots. |
| PointersUpdatingVisitor updating_visitor; |
| heap_->IterateRoots(&updating_visitor, VISIT_ALL_IN_SWEEP_NEWSPACE); |
| - UpdatePointersInParallel<OLD_TO_NEW>(heap_, &page_parallel_job_semaphore_); |
| + UpdatePointersInParallel<OLD_TO_NEW>(heap_, &page_parallel_job_semaphore_, |
| + this); |
| } |
| { |
| Heap* heap = this->heap(); |
| TRACE_GC(heap->tracer(), |
| GCTracer::Scope::MC_EVACUATE_UPDATE_POINTERS_TO_EVACUATED); |
| - UpdatePointersInParallel<OLD_TO_OLD>(heap_, &page_parallel_job_semaphore_); |
| + UpdatePointersInParallel<OLD_TO_OLD>(heap_, &page_parallel_job_semaphore_, |
| + this); |
| } |
| { |
| @@ -4299,7 +4408,8 @@ void MinorMarkCompactCollector::UpdatePointersAfterEvacuation() { |
| // global handles. Find a way to only process the ones related to new |
| // space. |
| heap_->IterateRoots(&updating_visitor, VISIT_ALL_IN_SWEEP_NEWSPACE); |
| - UpdatePointersInParallel<OLD_TO_NEW>(heap_, &page_parallel_job_semaphore_); |
| + UpdatePointersInParallel<OLD_TO_NEW>(heap_, &page_parallel_job_semaphore_, |
| + this); |
| } |
| { |
| @@ -4357,7 +4467,7 @@ int MarkCompactCollector::Sweeper::ParallelSweepPage(Page* page, |
| DCHECK_EQ(Page::kSweepingPending, |
| page->concurrent_sweeping_state().Value()); |
| page->concurrent_sweeping_state().SetValue(Page::kSweepingInProgress); |
| - const Sweeper::FreeSpaceTreatmentMode free_space_mode = |
| + const FreeSpaceTreatmentMode free_space_mode = |
| Heap::ShouldZapGarbage() ? ZAP_FREE_SPACE : IGNORE_FREE_SPACE; |
| if (identity == NEW_SPACE) { |
| RawSweep(page, IGNORE_FREE_LIST, free_space_mode); |
| @@ -4442,8 +4552,9 @@ void MarkCompactCollector::StartSweepSpace(PagedSpace* space) { |
| // testing this is fine. |
| p->concurrent_sweeping_state().SetValue(Page::kSweepingInProgress); |
| Sweeper::RawSweep(p, Sweeper::IGNORE_FREE_LIST, |
| - Heap::ShouldZapGarbage() ? Sweeper::ZAP_FREE_SPACE |
| - : Sweeper::IGNORE_FREE_SPACE); |
| + Heap::ShouldZapGarbage() |
| + ? FreeSpaceTreatmentMode::ZAP_FREE_SPACE |
| + : FreeSpaceTreatmentMode::IGNORE_FREE_SPACE); |
| continue; |
| } |