Index: src/heap/spaces.cc |
diff --git a/src/heap/spaces.cc b/src/heap/spaces.cc |
index f98120bd5066fd279083079912c4ab153796062d..8f75d5fd630b1a7c627de902d79850807b23845a 100644 |
--- a/src/heap/spaces.cc |
+++ b/src/heap/spaces.cc |
@@ -80,7 +80,8 @@ |
code_range_(NULL), |
free_list_(0), |
allocation_list_(0), |
- current_allocation_block_index_(0) {} |
+ current_allocation_block_index_(0), |
+ emergency_block_() {} |
bool CodeRange::SetUp(size_t requested) { |
@@ -139,6 +140,7 @@ |
current_allocation_block_index_ = 0; |
LOG(isolate_, NewEvent("CodeRange", code_range_->address(), requested)); |
+ ReserveEmergencyBlock(); |
return true; |
} |
@@ -271,6 +273,24 @@ |
void CodeRange::ReleaseBlock(const FreeBlock* block) { |
base::LockGuard<base::Mutex> guard(&code_range_mutex_); |
free_list_.Add(*block); |
+} |
+ |
+ |
+void CodeRange::ReserveEmergencyBlock() { |
+ const size_t requested_size = MemoryAllocator::CodePageAreaSize(); |
+ if (emergency_block_.size == 0) { |
+ ReserveBlock(requested_size, &emergency_block_); |
+ } else { |
+ DCHECK(emergency_block_.size >= requested_size); |
+ } |
+} |
+ |
+ |
+void CodeRange::ReleaseEmergencyBlock() { |
+ if (emergency_block_.size != 0) { |
+ ReleaseBlock(&emergency_block_); |
+ emergency_block_.size = 0; |
+ } |
} |
@@ -472,7 +492,6 @@ |
chunk->progress_bar_ = 0; |
chunk->high_water_mark_.SetValue(static_cast<intptr_t>(area_start - base)); |
chunk->set_parallel_sweeping(SWEEPING_DONE); |
- chunk->parallel_compaction_state().SetValue(kCompactingDone); |
chunk->mutex_ = NULL; |
chunk->available_in_small_free_list_ = 0; |
chunk->available_in_medium_free_list_ = 0; |
@@ -955,7 +974,8 @@ |
: Space(heap, space, executable), |
free_list_(this), |
unswept_free_bytes_(0), |
- end_of_unswept_pages_(NULL) { |
+ end_of_unswept_pages_(NULL), |
+ emergency_memory_(NULL) { |
area_size_ = MemoryAllocator::PageAreaSize(space); |
accounting_stats_.Clear(); |
@@ -983,37 +1003,30 @@ |
} |
-void PagedSpace::MoveOverFreeMemory(PagedSpace* other) { |
- DCHECK(identity() == other->identity()); |
- // Destroy the linear allocation space of {other}. This is needed to |
- // (a) not waste the memory and |
- // (b) keep the rest of the chunk in an iterable state (filler is needed). |
- other->EmptyAllocationInfo(); |
- |
- // Move over the free list. Concatenate makes sure that the source free list |
- // gets properly reset after moving over all nodes. |
- intptr_t freed_bytes = free_list_.Concatenate(other->free_list()); |
- other->accounting_stats_.AllocateBytes(freed_bytes); |
- // We do not adjust accounting_stats_ for {this} as we treat the received |
- // memory as borrowed, i.e., the originating space keeps track of its |
- // capacity. Other stats, e.g. accounting_stats_.{size_,waste_} are properly |
- // maintained by allocating and freeing blocks. |
-} |
- |
- |
void PagedSpace::MergeCompactionSpace(CompactionSpace* other) { |
// Unmerged fields: |
// area_size_ |
// allocation_info_ |
+ // emergency_memory_ |
// end_of_unswept_pages_ |
// unswept_free_bytes_ |
// anchor_ |
- MoveOverFreeMemory(other); |
+ // It only makes sense to merge compatible spaces. |
+ DCHECK(identity() == other->identity()); |
+ |
+ // Destroy the linear allocation space of {other}. This is needed to (a) not |
+ // waste the memory and (b) keep the rest of the chunk in an iterable state |
+ // (filler is needed). |
+ int linear_size = static_cast<int>(other->limit() - other->top()); |
+ other->Free(other->top(), linear_size); |
+ |
+ // Move over the free list. |
+ free_list_.Concatenate(other->free_list()); |
// Update and clear accounting statistics. |
accounting_stats_.Merge(other->accounting_stats_); |
- other->accounting_stats_.Reset(); |
+ other->accounting_stats_.Clear(); |
// Move over pages. |
PageIterator it(other); |
@@ -1097,6 +1110,9 @@ |
if (!heap()->deserialization_complete()) p->MarkNeverEvacuate(); |
DCHECK(Capacity() <= heap()->MaxOldGenerationSize()); |
+ DCHECK(heap()->CommittedOldGenerationMemory() <= |
+ heap()->MaxOldGenerationSize() + |
+ PagedSpace::MaxEmergencyMemoryAllocated()); |
p->InsertAfter(anchor_.prev_page()); |
@@ -1163,6 +1179,51 @@ |
DCHECK(Capacity() > 0); |
accounting_stats_.ShrinkSpace(AreaSize()); |
+} |
+ |
+ |
+intptr_t PagedSpace::MaxEmergencyMemoryAllocated() { |
+ // New space and large object space. |
+ static const int spaces_without_emergency_memory = 2; |
+ static const int spaces_with_emergency_memory = |
+ LAST_SPACE - FIRST_SPACE + 1 - spaces_without_emergency_memory; |
+ return Page::kPageSize * spaces_with_emergency_memory; |
+} |
+ |
+ |
+void PagedSpace::CreateEmergencyMemory() { |
+ if (identity() == CODE_SPACE) { |
+ // Make the emergency block available to the allocator. |
+ CodeRange* code_range = heap()->isolate()->code_range(); |
+ if (code_range != NULL && code_range->valid()) { |
+ code_range->ReleaseEmergencyBlock(); |
+ } |
+ DCHECK(MemoryAllocator::CodePageAreaSize() == AreaSize()); |
+ } |
+ emergency_memory_ = heap()->isolate()->memory_allocator()->AllocateChunk( |
+ AreaSize(), AreaSize(), executable(), this); |
+} |
+ |
+ |
+void PagedSpace::FreeEmergencyMemory() { |
+ Page* page = static_cast<Page*>(emergency_memory_); |
+ DCHECK(page->LiveBytes() == 0); |
+ DCHECK(AreaSize() == page->area_size()); |
+ DCHECK(!free_list_.ContainsPageFreeListItems(page)); |
+ heap()->isolate()->memory_allocator()->Free(page); |
+ emergency_memory_ = NULL; |
+} |
+ |
+ |
+void PagedSpace::UseEmergencyMemory() { |
+ // Page::Initialize makes the chunk into a real page and adds it to the |
+ // accounting for this space. Unlike PagedSpace::Expand, we don't check |
+ // CanExpand first, so we can go over the limits a little here. That's OK, |
+ // because we are in the process of compacting which will free up at least as |
+ // much memory as it allocates. |
+ Page* page = Page::Initialize(heap(), emergency_memory_, executable(), this); |
+ page->InsertAfter(anchor_.prev_page()); |
+ emergency_memory_ = NULL; |
} |