Index: src/mark-compact.cc |
=================================================================== |
--- src/mark-compact.cc (revision 10059) |
+++ src/mark-compact.cc (working copy) |
@@ -418,26 +418,69 @@ |
space->identity() == OLD_DATA_SPACE || |
space->identity() == CODE_SPACE); |
+ int number_of_pages = space->CountTotalPages(); |
+ |
PageIterator it(space); |
+ const int kMaxMaxEvacuationCandidates = 1000; |
+ int max_evacuation_candidates = Min( |
+ kMaxMaxEvacuationCandidates, |
+ static_cast<int>(sqrt(number_of_pages / 2) + 1)); |
+ |
+ if (FLAG_stress_compaction || FLAG_always_compact) { |
+ max_evacuation_candidates = kMaxMaxEvacuationCandidates; |
+ } |
+ |
+ class Candidate { |
+ public: |
+ Candidate() : fragmentation_(0), page_(NULL) { } |
+ Candidate(int f, Page* p) : fragmentation_(f), page_(p) { } |
+ |
+ int fragmentation() { return fragmentation_; } |
+ Page* page() { return page_; } |
+ |
+ private: |
+ int fragmentation_; |
+ Page* page_; |
+ }; |
+ |
+ Candidate candidates[kMaxMaxEvacuationCandidates]; |
+ |
int count = 0; |
if (it.has_next()) it.next(); // Never compact the first page. |
+ int fragmentation = 0; |
+ Candidate* least = NULL; |
while (it.has_next()) { |
Page* p = it.next(); |
- bool evacuate = false; |
+ p->ClearEvacuationCandidate(); |
if (FLAG_stress_compaction) { |
int counter = space->heap()->ms_count(); |
uintptr_t page_number = reinterpret_cast<uintptr_t>(p) >> kPageSizeBits; |
- if ((counter & 1) == (page_number & 1)) evacuate = true; |
+ if ((counter & 1) == (page_number & 1)) fragmentation = 1; |
} else { |
- if (space->IsFragmented(p)) evacuate = true; |
+ fragmentation = space->Fragmentation(p); |
} |
- if (evacuate) { |
- AddEvacuationCandidate(p); |
- count++; |
- } else { |
- p->ClearEvacuationCandidate(); |
+ if (fragmentation != 0) { |
+ if (count < max_evacuation_candidates) { |
+ candidates[count++] = Candidate(fragmentation, p); |
+ } else { |
+ if (least == NULL) { |
+ for (int i = 0; i < max_evacuation_candidates; i++) { |
+ if (least == NULL || |
+ candidates[i].fragmentation() < least->fragmentation()) { |
+ least = candidates + i; |
+ } |
+ } |
+ } |
+ if (least->fragmentation() < fragmentation) { |
+ *least = Candidate(fragmentation, p); |
+ least = NULL; |
+ } |
+ } |
} |
} |
+ for (int i = 0; i < count; i++) { |
+ AddEvacuationCandidate(candidates[i].page()); |
+ } |
if (count > 0 && FLAG_trace_fragmentation) { |
PrintF("Collected %d evacuation candidates for space %s\n", |
@@ -2954,109 +2997,131 @@ |
void MarkCompactCollector::EvacuateNewSpaceAndCandidates() { |
- bool code_slots_filtering_required = MarkInvalidatedCode(); |
+ bool code_slots_filtering_required; |
+ { GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_SWEEP_NEWSPACE); |
+ code_slots_filtering_required = MarkInvalidatedCode(); |
- EvacuateNewSpace(); |
- EvacuatePages(); |
+ EvacuateNewSpace(); |
+ } |
+ |
+ { GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_EVACUATE_PAGES); |
+ EvacuatePages(); |
+ } |
+ |
// Second pass: find pointers to new space and update them. |
PointersUpdatingVisitor updating_visitor(heap()); |
- // Update pointers in to space. |
- SemiSpaceIterator to_it(heap()->new_space()->bottom(), |
- heap()->new_space()->top()); |
- for (HeapObject* object = to_it.Next(); |
- object != NULL; |
- object = to_it.Next()) { |
- Map* map = object->map(); |
- object->IterateBody(map->instance_type(), |
- object->SizeFromMap(map), |
- &updating_visitor); |
+ { GCTracer::Scope gc_scope(tracer_, |
+ GCTracer::Scope::MC_UPDATE_NEW_TO_NEW_POINTERS); |
+ // Update pointers in to space. |
+ SemiSpaceIterator to_it(heap()->new_space()->bottom(), |
+ heap()->new_space()->top()); |
+ for (HeapObject* object = to_it.Next(); |
+ object != NULL; |
+ object = to_it.Next()) { |
+ Map* map = object->map(); |
+ object->IterateBody(map->instance_type(), |
+ object->SizeFromMap(map), |
+ &updating_visitor); |
+ } |
} |
- // Update roots. |
- heap_->IterateRoots(&updating_visitor, VISIT_ALL_IN_SWEEP_NEWSPACE); |
- LiveObjectList::IterateElements(&updating_visitor); |
+ { GCTracer::Scope gc_scope(tracer_, |
+ GCTracer::Scope::MC_UPDATE_ROOT_TO_NEW_POINTERS); |
+ // Update roots. |
+ heap_->IterateRoots(&updating_visitor, VISIT_ALL_IN_SWEEP_NEWSPACE); |
+ LiveObjectList::IterateElements(&updating_visitor); |
+ } |
- { |
+ { GCTracer::Scope gc_scope(tracer_, |
+ GCTracer::Scope::MC_UPDATE_OLD_TO_NEW_POINTERS); |
StoreBufferRebuildScope scope(heap_, |
heap_->store_buffer(), |
&Heap::ScavengeStoreBufferCallback); |
heap_->store_buffer()->IteratePointersToNewSpace(&UpdatePointer); |
} |
- SlotsBuffer::UpdateSlotsRecordedIn(heap_, |
- migration_slots_buffer_, |
- code_slots_filtering_required); |
- if (FLAG_trace_fragmentation) { |
- PrintF(" migration slots buffer: %d\n", |
- SlotsBuffer::SizeOfChain(migration_slots_buffer_)); |
- } |
+ { GCTracer::Scope gc_scope(tracer_, |
+ GCTracer::Scope::MC_UPDATE_POINTERS_TO_EVACUATED); |
+ SlotsBuffer::UpdateSlotsRecordedIn(heap_, |
+ migration_slots_buffer_, |
+ code_slots_filtering_required); |
+ if (FLAG_trace_fragmentation) { |
+ PrintF(" migration slots buffer: %d\n", |
+ SlotsBuffer::SizeOfChain(migration_slots_buffer_)); |
+ } |
- if (compacting_ && was_marked_incrementally_) { |
- // It's difficult to filter out slots recorded for large objects. |
- LargeObjectIterator it(heap_->lo_space()); |
- for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { |
- // LargeObjectSpace is not swept yet thus we have to skip |
- // dead objects explicitly. |
- if (!IsMarked(obj)) continue; |
+ if (compacting_ && was_marked_incrementally_) { |
+ // It's difficult to filter out slots recorded for large objects. |
+ LargeObjectIterator it(heap_->lo_space()); |
+ for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { |
+ // LargeObjectSpace is not swept yet thus we have to skip |
+ // dead objects explicitly. |
+ if (!IsMarked(obj)) continue; |
- Page* p = Page::FromAddress(obj->address()); |
- if (p->IsFlagSet(Page::RESCAN_ON_EVACUATION)) { |
- obj->Iterate(&updating_visitor); |
- p->ClearFlag(Page::RESCAN_ON_EVACUATION); |
+ Page* p = Page::FromAddress(obj->address()); |
+ if (p->IsFlagSet(Page::RESCAN_ON_EVACUATION)) { |
+ obj->Iterate(&updating_visitor); |
+ p->ClearFlag(Page::RESCAN_ON_EVACUATION); |
+ } |
} |
} |
} |
int npages = evacuation_candidates_.length(); |
- for (int i = 0; i < npages; i++) { |
- Page* p = evacuation_candidates_[i]; |
- ASSERT(p->IsEvacuationCandidate() || |
- p->IsFlagSet(Page::RESCAN_ON_EVACUATION)); |
+ { GCTracer::Scope gc_scope( |
+ tracer_, GCTracer::Scope::MC_UPDATE_POINTERS_BETWEEN_EVACUATED); |
+ for (int i = 0; i < npages; i++) { |
+ Page* p = evacuation_candidates_[i]; |
+ ASSERT(p->IsEvacuationCandidate() || |
+ p->IsFlagSet(Page::RESCAN_ON_EVACUATION)); |
- if (p->IsEvacuationCandidate()) { |
- SlotsBuffer::UpdateSlotsRecordedIn(heap_, |
- p->slots_buffer(), |
- code_slots_filtering_required); |
- if (FLAG_trace_fragmentation) { |
- PrintF(" page %p slots buffer: %d\n", |
- reinterpret_cast<void*>(p), |
- SlotsBuffer::SizeOfChain(p->slots_buffer())); |
- } |
+ if (p->IsEvacuationCandidate()) { |
+ SlotsBuffer::UpdateSlotsRecordedIn(heap_, |
+ p->slots_buffer(), |
+ code_slots_filtering_required); |
+ if (FLAG_trace_fragmentation) { |
+ PrintF(" page %p slots buffer: %d\n", |
+ reinterpret_cast<void*>(p), |
+ SlotsBuffer::SizeOfChain(p->slots_buffer())); |
+ } |
- // Important: skip list should be cleared only after roots were updated |
- // because root iteration traverses the stack and might have to find code |
- // objects from non-updated pc pointing into evacuation candidate. |
- SkipList* list = p->skip_list(); |
- if (list != NULL) list->Clear(); |
- } else { |
- if (FLAG_gc_verbose) { |
- PrintF("Sweeping 0x%" V8PRIxPTR " during evacuation.\n", |
- reinterpret_cast<intptr_t>(p)); |
- } |
- PagedSpace* space = static_cast<PagedSpace*>(p->owner()); |
- p->ClearFlag(MemoryChunk::RESCAN_ON_EVACUATION); |
+ // Important: skip list should be cleared only after roots were updated |
+ // because root iteration traverses the stack and might have to find |
+ // code objects from non-updated pc pointing into evacuation candidate. |
+ SkipList* list = p->skip_list(); |
+ if (list != NULL) list->Clear(); |
+ } else { |
+ if (FLAG_gc_verbose) { |
+ PrintF("Sweeping 0x%" V8PRIxPTR " during evacuation.\n", |
+ reinterpret_cast<intptr_t>(p)); |
+ } |
+ PagedSpace* space = static_cast<PagedSpace*>(p->owner()); |
+ p->ClearFlag(MemoryChunk::RESCAN_ON_EVACUATION); |
- switch (space->identity()) { |
- case OLD_DATA_SPACE: |
- SweepConservatively(space, p); |
- break; |
- case OLD_POINTER_SPACE: |
- SweepPrecisely<SWEEP_AND_VISIT_LIVE_OBJECTS, IGNORE_SKIP_LIST>( |
- space, p, &updating_visitor); |
- break; |
- case CODE_SPACE: |
- SweepPrecisely<SWEEP_AND_VISIT_LIVE_OBJECTS, REBUILD_SKIP_LIST>( |
- space, p, &updating_visitor); |
- break; |
- default: |
- UNREACHABLE(); |
- break; |
+ switch (space->identity()) { |
+ case OLD_DATA_SPACE: |
+ SweepConservatively(space, p); |
+ break; |
+ case OLD_POINTER_SPACE: |
+ SweepPrecisely<SWEEP_AND_VISIT_LIVE_OBJECTS, IGNORE_SKIP_LIST>( |
+ space, p, &updating_visitor); |
+ break; |
+ case CODE_SPACE: |
+ SweepPrecisely<SWEEP_AND_VISIT_LIVE_OBJECTS, REBUILD_SKIP_LIST>( |
+ space, p, &updating_visitor); |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ break; |
+ } |
} |
} |
} |
+ GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_UPDATE_MISC_POINTERS); |
+ |
// Update pointers from cells. |
HeapObjectIterator cell_iterator(heap_->cell_space()); |
for (HeapObject* cell = cell_iterator.Next(); |
@@ -3483,8 +3548,7 @@ |
} |
-void MarkCompactCollector::SweepSpace(PagedSpace* space, |
- SweeperType sweeper) { |
+void MarkCompactCollector::SweepSpace(PagedSpace* space, SweeperType sweeper) { |
space->set_was_swept_conservatively(sweeper == CONSERVATIVE || |
sweeper == LAZY_CONSERVATIVE); |
@@ -3493,10 +3557,16 @@ |
PageIterator it(space); |
intptr_t freed_bytes = 0; |
+ int pages_swept = 0; |
intptr_t newspace_size = space->heap()->new_space()->Size(); |
bool lazy_sweeping_active = false; |
bool unused_page_present = false; |
+ intptr_t old_space_size = heap()->PromotedSpaceSize(); |
+ intptr_t space_left = |
+ Min(heap()->OldGenPromotionLimit(old_space_size), |
+ heap()->OldGenAllocationLimit(old_space_size)) - old_space_size; |
+ |
while (it.has_next()) { |
Page* p = it.next(); |
@@ -3535,31 +3605,45 @@ |
unused_page_present = true; |
} |
- if (FLAG_gc_verbose) { |
- PrintF("Sweeping 0x%" V8PRIxPTR " with sweeper %d.\n", |
- reinterpret_cast<intptr_t>(p), |
- sweeper); |
- } |
- |
switch (sweeper) { |
case CONSERVATIVE: { |
+ if (FLAG_gc_verbose) { |
+ PrintF("Sweeping 0x%" V8PRIxPTR " conservatively.\n", |
+ reinterpret_cast<intptr_t>(p)); |
+ } |
SweepConservatively(space, p); |
+ pages_swept++; |
break; |
} |
case LAZY_CONSERVATIVE: { |
+ if (FLAG_gc_verbose) { |
+ PrintF("Sweeping 0x%" V8PRIxPTR " conservatively as needed.\n", |
+ reinterpret_cast<intptr_t>(p)); |
+ } |
freed_bytes += SweepConservatively(space, p); |
- if (freed_bytes >= newspace_size && p != space->LastPage()) { |
+ pages_swept++; |
+ if (space_left + freed_bytes > newspace_size) { |
space->SetPagesToSweep(p->next_page()); |
lazy_sweeping_active = true; |
+ } else { |
+ if (FLAG_gc_verbose) { |
+ PrintF("Only %" V8PRIdPTR " bytes freed. Still sweeping.\n", |
+ freed_bytes); |
+ } |
} |
break; |
} |
case PRECISE: { |
+ if (FLAG_gc_verbose) { |
+ PrintF("Sweeping 0x%" V8PRIxPTR " precisely.\n", |
+ reinterpret_cast<intptr_t>(p)); |
+ } |
if (space->identity() == CODE_SPACE) { |
SweepPrecisely<SWEEP_ONLY, REBUILD_SKIP_LIST>(space, p, NULL); |
} else { |
SweepPrecisely<SWEEP_ONLY, IGNORE_SKIP_LIST>(space, p, NULL); |
} |
+ pages_swept++; |
break; |
} |
default: { |
@@ -3568,6 +3652,12 @@ |
} |
} |
+ if (FLAG_gc_verbose) { |
+ PrintF("SweepSpace: %s (%d pages swept)\n", |
+ AllocationSpaceName(space->identity()), |
+ pages_swept); |
+ } |
+ |
// Give pages that are queued to be freed back to the OS. |
heap()->FreeQueuedChunks(); |
} |
@@ -3594,9 +3684,7 @@ |
SweepSpace(heap()->cell_space(), PRECISE); |
- { GCTracer::Scope gc_scope(tracer_, GCTracer::Scope::MC_SWEEP_NEWSPACE); |
- EvacuateNewSpaceAndCandidates(); |
- } |
+ EvacuateNewSpaceAndCandidates(); |
// ClearNonLiveTransitions depends on precise sweeping of map space to |
// detect whether unmarked map became dead in this collection or in one |