From d703e87531e9565ca29e8b76dec9213397ac1536 Mon Sep 17 00:00:00 2001 From: ulan Date: Thu, 20 Nov 2014 06:52:23 -0800 Subject: [PATCH] Reserve code range block for evacuation. If we run out of code range, then GC wouldn't be able to compact code space, because it wouldn't be able to allocate a new page. This can cause code space fragmentation and OOM crashes. BUG=chromium:430118 LOG=Y Review URL: https://codereview.chromium.org/742733002 Cr-Commit-Position: refs/heads/master@{#25441} --- src/heap/mark-compact.cc | 4 +++ src/heap/spaces.cc | 82 +++++++++++++++++++++++++++++++++------------- src/heap/spaces.h | 12 +++++++ test/cctest/test-spaces.cc | 3 +- 4 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc index 6c005cb..9c481b0 100644 --- a/src/heap/mark-compact.cc +++ b/src/heap/mark-compact.cc @@ -4179,6 +4179,10 @@ void MarkCompactCollector::SweepSpaces() { // Deallocate evacuated candidate pages. ReleaseEvacuationCandidates(); + CodeRange* code_range = heap()->isolate()->code_range(); + if (code_range != NULL && code_range->valid()) { + code_range->ReserveEmergencyBlock(); + } if (FLAG_print_cumulative_gc_stat) { heap_->tracer()->AddSweepingTime(base::OS::TimeCurrentMillis() - diff --git a/src/heap/spaces.cc b/src/heap/spaces.cc index 30b141f..51a0ef6 100644 --- a/src/heap/spaces.cc +++ b/src/heap/spaces.cc @@ -93,7 +93,8 @@ CodeRange::CodeRange(Isolate* isolate) 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) { @@ -144,6 +145,7 @@ bool CodeRange::SetUp(size_t requested) { current_allocation_block_index_ = 0; LOG(isolate_, NewEvent("CodeRange", code_range_->address(), requested)); + ReserveEmergencyBlock(); return true; } @@ -202,35 +204,20 @@ Address CodeRange::AllocateRawMemory(const size_t requested_size, const size_t commit_size, size_t* allocated) { DCHECK(commit_size <= requested_size); - DCHECK(allocation_list_.length() == 0 || - current_allocation_block_index_ < allocation_list_.length()); - if (allocation_list_.length() == 0 || - requested_size > allocation_list_[current_allocation_block_index_].size) { - // Find an allocation block large enough. - if (!GetNextAllocationBlock(requested_size)) return NULL; - } - // Commit the requested memory at the start of the current allocation block. - size_t aligned_requested = RoundUp(requested_size, MemoryChunk::kAlignment); - FreeBlock current = allocation_list_[current_allocation_block_index_]; - if (aligned_requested >= (current.size - Page::kPageSize)) { - // Don't leave a small free block, useless for a large object or chunk. - *allocated = current.size; - } else { - *allocated = aligned_requested; + FreeBlock current; + if (!ReserveBlock(requested_size, ¤t)) { + *allocated = 0; + return NULL; } + *allocated = current.size; DCHECK(*allocated <= current.size); DCHECK(IsAddressAligned(current.start, MemoryChunk::kAlignment)); if (!isolate_->memory_allocator()->CommitExecutableMemory( code_range_, current.start, commit_size, *allocated)) { *allocated = 0; + ReleaseBlock(¤t); return NULL; } - allocation_list_[current_allocation_block_index_].start += *allocated; - allocation_list_[current_allocation_block_index_].size -= *allocated; - if (*allocated == current.size) { - // This block is used up, get the next one. - GetNextAllocationBlock(0); - } return current.start; } @@ -260,6 +247,49 @@ void CodeRange::TearDown() { } +bool CodeRange::ReserveBlock(const size_t requested_size, FreeBlock* block) { + DCHECK(allocation_list_.length() == 0 || + current_allocation_block_index_ < allocation_list_.length()); + if (allocation_list_.length() == 0 || + requested_size > allocation_list_[current_allocation_block_index_].size) { + // Find an allocation block large enough. + if (!GetNextAllocationBlock(requested_size)) return false; + } + // Commit the requested memory at the start of the current allocation block. + size_t aligned_requested = RoundUp(requested_size, MemoryChunk::kAlignment); + *block = allocation_list_[current_allocation_block_index_]; + // Don't leave a small free block, useless for a large object or chunk. + if (aligned_requested < (block->size - Page::kPageSize)) { + block->size = aligned_requested; + } + DCHECK(IsAddressAligned(block->start, MemoryChunk::kAlignment)); + allocation_list_[current_allocation_block_index_].start += block->size; + allocation_list_[current_allocation_block_index_].size -= block->size; + return true; +} + + +void CodeRange::ReleaseBlock(const FreeBlock* block) { 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; + } +} + + // ----------------------------------------------------------------------------- // MemoryAllocator // @@ -1106,6 +1136,14 @@ void PagedSpace::ReleasePage(Page* page) { 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); } diff --git a/src/heap/spaces.h b/src/heap/spaces.h index ef294b2..dcd3364 100644 --- a/src/heap/spaces.h +++ b/src/heap/spaces.h @@ -900,6 +900,9 @@ class CodeRange { bool UncommitRawMemory(Address start, size_t length); void FreeRawMemory(Address buf, size_t length); + void ReserveEmergencyBlock(); + void ReleaseEmergencyBlock(); + private: Isolate* isolate_; @@ -908,6 +911,7 @@ class CodeRange { // Plain old data class, just a struct plus a constructor. class FreeBlock { public: + FreeBlock() : start(0), size(0) {} FreeBlock(Address start_arg, size_t size_arg) : start(start_arg), size(size_arg) { DCHECK(IsAddressAligned(start, MemoryChunk::kAlignment)); @@ -932,6 +936,12 @@ class CodeRange { List allocation_list_; int current_allocation_block_index_; + // Emergency block guarantees that we can always allocate a page for + // evacuation candidates when code space is compacted. Emergency block is + // reserved immediately after GC and is released immedietely before + // allocating a page for evacuation. + FreeBlock emergency_block_; + // Finds a block on the allocation list that contains at least the // requested amount of memory. If none is found, sorts and merges // the existing free memory blocks, and searches again. @@ -940,6 +950,8 @@ class CodeRange { // Compares the start addresses of two free blocks. static int CompareFreeBlockAddress(const FreeBlock* left, const FreeBlock* right); + bool ReserveBlock(const size_t requested_size, FreeBlock* block); + void ReleaseBlock(const FreeBlock* block); DISALLOW_COPY_AND_ASSIGN(CodeRange); }; diff --git a/test/cctest/test-spaces.cc b/test/cctest/test-spaces.cc index 9ad4423..e79063b 100644 --- a/test/cctest/test-spaces.cc +++ b/test/cctest/test-spaces.cc @@ -216,7 +216,8 @@ TEST(Regress3540) { if (!code_range->SetUp( code_range_size + RoundUp(v8::base::OS::CommitPageSize() * kReservedCodeRangePages, - MemoryChunk::kAlignment))) { + MemoryChunk::kAlignment) + + v8::internal::MemoryAllocator::CodePageAreaSize())) { return; } Address address; -- 2.7.4