| Index: src/mark-compact.cc
|
| ===================================================================
|
| --- src/mark-compact.cc (revision 10927)
|
| +++ src/mark-compact.cc (working copy)
|
| @@ -230,17 +230,36 @@
|
| }
|
|
|
|
|
| -bool MarkCompactCollector::StartCompaction() {
|
| +static void TraceFragmentation(PagedSpace* space) {
|
| + int number_of_pages = space->CountTotalPages();
|
| + intptr_t reserved = (number_of_pages * space->AreaSize());
|
| + intptr_t free = reserved - space->SizeOfObjects();
|
| + PrintF("[%s]: %d pages, %d (%.1f%%) free\n",
|
| + AllocationSpaceName(space->identity()),
|
| + number_of_pages,
|
| + static_cast<int>(free),
|
| + static_cast<double>(free) * 100 / reserved);
|
| +}
|
| +
|
| +
|
| +bool MarkCompactCollector::StartCompaction(CompactionMode mode) {
|
| if (!compacting_) {
|
| ASSERT(evacuation_candidates_.length() == 0);
|
|
|
| CollectEvacuationCandidates(heap()->old_pointer_space());
|
| CollectEvacuationCandidates(heap()->old_data_space());
|
|
|
| - if (FLAG_compact_code_space) {
|
| + if (mode == NON_INCREMENTAL_COMPACTION) {
|
| CollectEvacuationCandidates(heap()->code_space());
|
| + } else if (FLAG_trace_fragmentation) {
|
| + TraceFragmentation(heap()->code_space());
|
| }
|
|
|
| + if (FLAG_trace_fragmentation) {
|
| + TraceFragmentation(heap()->map_space());
|
| + TraceFragmentation(heap()->cell_space());
|
| + }
|
| +
|
| heap()->old_pointer_space()->EvictEvacuationCandidatesFromFreeLists();
|
| heap()->old_data_space()->EvictEvacuationCandidatesFromFreeLists();
|
| heap()->code_space()->EvictEvacuationCandidatesFromFreeLists();
|
| @@ -414,6 +433,66 @@
|
| }
|
|
|
|
|
| +// Returns zero for pages that have so little fragmentation that it is not
|
| +// worth defragmenting them. Otherwise a positive integer that gives an
|
| +// estimate of fragmentation on an arbitrary scale.
|
| +static int FreeListFragmentation(PagedSpace* space, Page* p) {
|
| + // If page was not swept then there are no free list items on it.
|
| + if (!p->WasSwept()) {
|
| + if (FLAG_trace_fragmentation) {
|
| + PrintF("%p [%s]: %d bytes live (unswept)\n",
|
| + reinterpret_cast<void*>(p),
|
| + AllocationSpaceName(space->identity()),
|
| + p->LiveBytes());
|
| + }
|
| + return 0;
|
| + }
|
| +
|
| + FreeList::SizeStats sizes;
|
| + space->CountFreeListItems(p, &sizes);
|
| +
|
| + intptr_t ratio;
|
| + intptr_t ratio_threshold;
|
| + int area_size = space->AreaSize();
|
| + if (space->identity() == CODE_SPACE) {
|
| + ratio = (sizes.medium_size_ * 10 + sizes.large_size_ * 2) * 100 /
|
| + area_size;
|
| + ratio_threshold = 10;
|
| + } else {
|
| + ratio = (sizes.small_size_ * 5 + sizes.medium_size_) * 100 /
|
| + area_size;
|
| + ratio_threshold = 15;
|
| + }
|
| +
|
| + if (FLAG_trace_fragmentation) {
|
| + PrintF("%p [%s]: %d (%.2f%%) %d (%.2f%%) %d (%.2f%%) %d (%.2f%%) %s\n",
|
| + reinterpret_cast<void*>(p),
|
| + AllocationSpaceName(space->identity()),
|
| + static_cast<int>(sizes.small_size_),
|
| + static_cast<double>(sizes.small_size_ * 100) /
|
| + area_size,
|
| + static_cast<int>(sizes.medium_size_),
|
| + static_cast<double>(sizes.medium_size_ * 100) /
|
| + area_size,
|
| + static_cast<int>(sizes.large_size_),
|
| + static_cast<double>(sizes.large_size_ * 100) /
|
| + area_size,
|
| + static_cast<int>(sizes.huge_size_),
|
| + static_cast<double>(sizes.huge_size_ * 100) /
|
| + area_size,
|
| + (ratio > ratio_threshold) ? "[fragmented]" : "");
|
| + }
|
| +
|
| + if (FLAG_always_compact && sizes.Total() != area_size) {
|
| + return 1;
|
| + }
|
| +
|
| + if (ratio <= ratio_threshold) return 0; // Not fragmented.
|
| +
|
| + return static_cast<int>(ratio - ratio_threshold);
|
| +}
|
| +
|
| +
|
| void MarkCompactCollector::CollectEvacuationCandidates(PagedSpace* space) {
|
| ASSERT(space->identity() == OLD_POINTER_SPACE ||
|
| space->identity() == OLD_DATA_SPACE ||
|
| @@ -421,7 +500,6 @@
|
|
|
| int number_of_pages = space->CountTotalPages();
|
|
|
| - PageIterator it(space);
|
| const int kMaxMaxEvacuationCandidates = 1000;
|
| int max_evacuation_candidates = Min(
|
| kMaxMaxEvacuationCandidates,
|
| @@ -444,22 +522,89 @@
|
| Page* page_;
|
| };
|
|
|
| + enum CompactionMode {
|
| + COMPACT_FREE_LISTS,
|
| + REDUCE_MEMORY_FOOTPRINT
|
| + };
|
| +
|
| + CompactionMode mode = COMPACT_FREE_LISTS;
|
| +
|
| + intptr_t reserved = number_of_pages * space->AreaSize();
|
| + intptr_t over_reserved = reserved - space->SizeOfObjects();
|
| + static const intptr_t kFreenessThreshold = 50;
|
| +
|
| + if (over_reserved >= 2 * space->AreaSize() &&
|
| + reduce_memory_footprint_) {
|
| + mode = REDUCE_MEMORY_FOOTPRINT;
|
| +
|
| + // We expect that empty pages are easier to compact so slightly bump the
|
| + // limit.
|
| + max_evacuation_candidates += 2;
|
| +
|
| + if (FLAG_trace_fragmentation) {
|
| + PrintF("Estimated over reserved memory: %.1f MB (setting threshold %d)\n",
|
| + static_cast<double>(over_reserved) / MB,
|
| + static_cast<int>(kFreenessThreshold));
|
| + }
|
| + }
|
| +
|
| + intptr_t estimated_release = 0;
|
| +
|
| Candidate candidates[kMaxMaxEvacuationCandidates];
|
|
|
| int count = 0;
|
| - if (it.has_next()) it.next(); // Never compact the first page.
|
| int fragmentation = 0;
|
| Candidate* least = NULL;
|
| +
|
| + PageIterator it(space);
|
| + if (it.has_next()) it.next(); // Never compact the first page.
|
| +
|
| while (it.has_next()) {
|
| Page* p = it.next();
|
| 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)) fragmentation = 1;
|
| + } else if (mode == REDUCE_MEMORY_FOOTPRINT) {
|
| + // Don't try to release too many pages.
|
| + if (estimated_release >= ((over_reserved * 3) / 4)) {
|
| + continue;
|
| + }
|
| +
|
| + intptr_t free_bytes = 0;
|
| +
|
| + if (!p->WasSwept()) {
|
| + free_bytes = (p->size() - p->LiveBytes());
|
| + } else {
|
| + FreeList::SizeStats sizes;
|
| + space->CountFreeListItems(p, &sizes);
|
| + free_bytes = sizes.Total();
|
| + }
|
| +
|
| + int free_pct = static_cast<int>(free_bytes * 100 / p->area_size());
|
| +
|
| + if (free_pct >= kFreenessThreshold) {
|
| + estimated_release += p->area_size() +
|
| + (p->area_size() - free_bytes);
|
| + fragmentation = free_pct;
|
| + } else {
|
| + fragmentation = 0;
|
| + }
|
| +
|
| + if (FLAG_trace_fragmentation) {
|
| + PrintF("%p [%s]: %d (%.2f%%) free %s\n",
|
| + reinterpret_cast<void*>(p),
|
| + AllocationSpaceName(space->identity()),
|
| + static_cast<int>(free_bytes),
|
| + static_cast<double>(free_bytes * 100) / p->area_size(),
|
| + (fragmentation > 0) ? "[fragmented]" : "");
|
| + }
|
| } else {
|
| - fragmentation = space->Fragmentation(p);
|
| + fragmentation = FreeListFragmentation(space, p);
|
| }
|
| +
|
| if (fragmentation != 0) {
|
| if (count < max_evacuation_candidates) {
|
| candidates[count++] = Candidate(fragmentation, p);
|
| @@ -479,6 +624,7 @@
|
| }
|
| }
|
| }
|
| +
|
| for (int i = 0; i < count; i++) {
|
| AddEvacuationCandidate(candidates[i].page());
|
| }
|
| @@ -552,7 +698,7 @@
|
| // Don't start compaction if we are in the middle of incremental
|
| // marking cycle. We did not collect any slots.
|
| if (!FLAG_never_compact && !was_marked_incrementally_) {
|
| - StartCompaction();
|
| + StartCompaction(NON_INCREMENTAL_COMPACTION);
|
| }
|
|
|
| PagedSpaces spaces;
|
| @@ -664,6 +810,8 @@
|
| isolate_->heap()->mark_compact_collector()->
|
| RecordCodeEntrySlot(slot, target);
|
|
|
| + RecordSharedFunctionInfoCodeSlot(shared);
|
| +
|
| candidate = next_candidate;
|
| }
|
|
|
| @@ -686,12 +834,21 @@
|
| candidate->set_code(lazy_compile);
|
| }
|
|
|
| + RecordSharedFunctionInfoCodeSlot(candidate);
|
| +
|
| candidate = next_candidate;
|
| }
|
|
|
| shared_function_info_candidates_head_ = NULL;
|
| }
|
|
|
| + void RecordSharedFunctionInfoCodeSlot(SharedFunctionInfo* shared) {
|
| + Object** slot = HeapObject::RawField(shared,
|
| + SharedFunctionInfo::kCodeOffset);
|
| + isolate_->heap()->mark_compact_collector()->
|
| + RecordSlot(slot, slot, HeapObject::cast(*slot));
|
| + }
|
| +
|
| static JSFunction** GetNextCandidateField(JSFunction* candidate) {
|
| return reinterpret_cast<JSFunction**>(
|
| candidate->address() + JSFunction::kCodeEntryOffset);
|
| @@ -1168,6 +1325,16 @@
|
| re->SetDataAtUnchecked(JSRegExp::saved_code_index(is_ascii),
|
| code,
|
| heap);
|
| +
|
| + // Saving a copy might create a pointer into compaction candidate
|
| + // that was not observed by marker. This might happen if JSRegExp data
|
| + // was marked through the compilation cache before marker reached JSRegExp
|
| + // object.
|
| + FixedArray* data = FixedArray::cast(re->data());
|
| + Object** slot = data->data_start() + JSRegExp::saved_code_index(is_ascii);
|
| + heap->mark_compact_collector()->
|
| + RecordSlot(slot, slot, code);
|
| +
|
| // Set a number in the 0-255 range to guarantee no smi overflow.
|
| re->SetDataAtUnchecked(JSRegExp::code_index(is_ascii),
|
| Smi::FromInt(heap->sweep_generation() & 0xff),
|
| @@ -3251,6 +3418,8 @@
|
| p->set_scan_on_scavenge(false);
|
| slots_buffer_allocator_.DeallocateChain(p->slots_buffer_address());
|
| p->ClearEvacuationCandidate();
|
| + p->ResetLiveBytes();
|
| + space->ReleasePage(p);
|
| }
|
| evacuation_candidates_.Rewind(0);
|
| compacting_ = false;
|
|
|