From: jochen@chromium.org Date: Wed, 22 Oct 2014 06:58:38 +0000 (+0000) Subject: Add support for a target new space size X-Git-Tag: upstream/4.7.83~6202 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8f7a455fe2278783a25590b9aeffa133f058932e;p=platform%2Fupstream%2Fv8.git Add support for a target new space size When this flag is set, we will commit additional pages up until the target size before doing a scavenge. I made sure that all tests pass when a target size of 16MB is set. BUG=v8:3626 R=ulan@chromium.org LOG=n Review URL: https://codereview.chromium.org/652543007 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@24790 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/flag-definitions.h b/src/flag-definitions.h index f5fdb8e..fc38ba4 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -509,6 +509,8 @@ DEFINE_BOOL(always_inline_smi_code, false, DEFINE_INT(min_semi_space_size, 0, "min size of a semi-space (in MBytes), the new space consists of two" "semi-spaces") +DEFINE_INT(target_semi_space_size, 0, + "target size of a semi-space (in MBytes) before triggering a GC") DEFINE_INT(max_semi_space_size, 0, "max size of a semi-space (in MBytes), the new space consists of two" "semi-spaces") diff --git a/src/heap/heap.cc b/src/heap/heap.cc index 2c7fc58..6e566c0 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc @@ -61,6 +61,7 @@ Heap::Heap() reserved_semispace_size_(8 * (kPointerSize / 4) * MB), max_semi_space_size_(8 * (kPointerSize / 4) * MB), initial_semispace_size_(Page::kPageSize), + target_semispace_size_(Page::kPageSize), max_old_generation_size_(700ul * (kPointerSize / 4) * MB), max_executable_size_(256ul * (kPointerSize / 4) * MB), // Variables set based on semispace_size_ and old_generation_size_ in @@ -4944,9 +4945,9 @@ bool Heap::ConfigureHeap(int max_semi_space_size, int max_old_space_size, initial_semispace_size_ = max_semi_space_size_; if (FLAG_trace_gc) { PrintPID( - "Min semi-space size cannot be more than the maximum" + "Min semi-space size cannot be more than the maximum " "semi-space size of %d MB\n", - max_semi_space_size_); + max_semi_space_size_ / MB); } } else { initial_semispace_size_ = initial_semispace_size; @@ -4955,6 +4956,31 @@ bool Heap::ConfigureHeap(int max_semi_space_size, int max_old_space_size, initial_semispace_size_ = Min(initial_semispace_size_, max_semi_space_size_); + if (FLAG_target_semi_space_size > 0) { + int target_semispace_size = FLAG_target_semi_space_size * MB; + if (target_semispace_size < initial_semispace_size_) { + target_semispace_size_ = initial_semispace_size_; + if (FLAG_trace_gc) { + PrintPID( + "Target semi-space size cannot be less than the minimum " + "semi-space size of %d MB\n", + initial_semispace_size_ / MB); + } + } else if (target_semispace_size > max_semi_space_size_) { + target_semispace_size_ = max_semi_space_size_; + if (FLAG_trace_gc) { + PrintPID( + "Target semi-space size cannot be less than the maximum " + "semi-space size of %d MB\n", + max_semi_space_size_ / MB); + } + } else { + target_semispace_size_ = target_semispace_size; + } + } + + target_semispace_size_ = Max(initial_semispace_size_, target_semispace_size_); + // The old generation is paged and needs at least one page for each space. int paged_space_count = LAST_PAGED_SPACE - FIRST_PAGED_SPACE + 1; max_old_generation_size_ = diff --git a/src/heap/heap.h b/src/heap/heap.h index 695ad8a..c0848c9 100644 --- a/src/heap/heap.h +++ b/src/heap/heap.h @@ -570,6 +570,7 @@ class Heap { int MaxSemiSpaceSize() { return max_semi_space_size_; } int ReservedSemiSpaceSize() { return reserved_semispace_size_; } int InitialSemiSpaceSize() { return initial_semispace_size_; } + int TargetSemiSpaceSize() { return target_semispace_size_; } intptr_t MaxOldGenerationSize() { return max_old_generation_size_; } intptr_t MaxExecutableSize() { return max_executable_size_; } @@ -1472,6 +1473,7 @@ class Heap { int reserved_semispace_size_; int max_semi_space_size_; int initial_semispace_size_; + int target_semispace_size_; intptr_t max_old_generation_size_; intptr_t max_executable_size_; intptr_t maximum_committed_; diff --git a/src/heap/spaces.cc b/src/heap/spaces.cc index 62f6d62..85281aa 100644 --- a/src/heap/spaces.cc +++ b/src/heap/spaces.cc @@ -1195,6 +1195,8 @@ bool NewSpace::SetUp(int reserved_semispace_capacity, // this chunk must be a power of two and it must be aligned to its size. int initial_semispace_capacity = heap()->InitialSemiSpaceSize(); + int target_semispace_capacity = heap()->TargetSemiSpaceSize(); + size_t size = 2 * reserved_semispace_capacity; Address base = heap()->isolate()->memory_allocator()->ReserveAlignedMemory( size, size, &reservation_); @@ -1223,9 +1225,10 @@ bool NewSpace::SetUp(int reserved_semispace_capacity, DCHECK(IsAddressAligned(chunk_base_, 2 * reserved_semispace_capacity, 0)); to_space_.SetUp(chunk_base_, initial_semispace_capacity, - maximum_semispace_capacity); + target_semispace_capacity, maximum_semispace_capacity); from_space_.SetUp(chunk_base_ + reserved_semispace_capacity, - initial_semispace_capacity, maximum_semispace_capacity); + initial_semispace_capacity, target_semispace_capacity, + maximum_semispace_capacity); if (!to_space_.Commit()) { return false; } @@ -1285,7 +1288,7 @@ void NewSpace::Grow() { if (!to_space_.ShrinkTo(from_space_.TotalCapacity())) { // We are in an inconsistent state because we could not // commit/uncommit memory from new space. - V8::FatalProcessOutOfMemory("Failed to grow new space."); + CHECK(false); } } } @@ -1293,6 +1296,36 @@ void NewSpace::Grow() { } +bool NewSpace::GrowOnePage() { + if (TotalCapacity() == MaximumCapacity()) return false; + int new_capacity = static_cast(TotalCapacity()) + Page::kPageSize; + if (to_space_.GrowTo(new_capacity)) { + // Only grow from space if we managed to grow to-space and the from space + // is actually committed. + if (from_space_.is_committed()) { + if (!from_space_.GrowTo(new_capacity)) { + // If we managed to grow to-space but couldn't grow from-space, + // attempt to shrink to-space. + if (!to_space_.ShrinkTo(from_space_.TotalCapacity())) { + // We are in an inconsistent state because we could not + // commit/uncommit memory from new space. + CHECK(false); + } + return false; + } + } else { + if (!from_space_.SetTotalCapacity(new_capacity)) { + // Can't really happen, but better safe than sorry. + CHECK(false); + } + } + DCHECK_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); + return true; + } + return false; +} + + void NewSpace::Shrink() { int new_capacity = Max(InitialTotalCapacity(), 2 * SizeAsInt()); int rounded_new_capacity = RoundUp(new_capacity, Page::kPageSize); @@ -1306,7 +1339,7 @@ void NewSpace::Shrink() { if (!to_space_.GrowTo(from_space_.TotalCapacity())) { // We are in an inconsistent state because we could not // commit/uncommit memory from new space. - V8::FatalProcessOutOfMemory("Failed to shrink new space."); + CHECK(false); } } } @@ -1367,8 +1400,19 @@ bool NewSpace::AddFreshPage() { return false; } if (!to_space_.AdvancePage()) { - // Failed to get a new page in to-space. - return false; + // Check if we reached the target capacity yet. If not, try to commit a page + // and continue. + if ((to_space_.TotalCapacity() < to_space_.TargetCapacity()) && + GrowOnePage()) { + if (!to_space_.AdvancePage()) { + // It doesn't make sense that we managed to commit a page, but can't use + // it. + CHECK(false); + } + } else { + // Failed to get a new page in to-space. + return false; + } } // Clear remainder of current page. @@ -1472,7 +1516,7 @@ void NewSpace::Verify() { // ----------------------------------------------------------------------------- // SemiSpace implementation -void SemiSpace::SetUp(Address start, int initial_capacity, +void SemiSpace::SetUp(Address start, int initial_capacity, int target_capacity, int maximum_capacity) { // Creates a space in the young generation. The constructor does not // allocate memory from the OS. A SemiSpace is given a contiguous chunk of @@ -1481,8 +1525,11 @@ void SemiSpace::SetUp(Address start, int initial_capacity, // space is used as the marking stack. It requires contiguous memory // addresses. DCHECK(maximum_capacity >= Page::kPageSize); + DCHECK(initial_capacity <= target_capacity); + DCHECK(target_capacity <= maximum_capacity); initial_total_capacity_ = RoundDown(initial_capacity, Page::kPageSize); total_capacity_ = initial_capacity; + target_capacity_ = RoundDown(target_capacity, Page::kPageSize); maximum_total_capacity_ = RoundDown(maximum_capacity, Page::kPageSize); maximum_committed_ = 0; committed_ = false; @@ -1611,6 +1658,17 @@ bool SemiSpace::ShrinkTo(int new_capacity) { } +bool SemiSpace::SetTotalCapacity(int new_capacity) { + CHECK(!is_committed()); + if (new_capacity >= initial_total_capacity_ && + new_capacity <= maximum_total_capacity_) { + total_capacity_ = new_capacity; + return true; + } + return false; +} + + void SemiSpace::FlipPages(intptr_t flags, intptr_t mask) { anchor_.set_owner(this); // Fixup back-pointers to anchor. Address of anchor changes diff --git a/src/heap/spaces.h b/src/heap/spaces.h index ef55357..5be9c3a 100644 --- a/src/heap/spaces.h +++ b/src/heap/spaces.h @@ -2075,7 +2075,8 @@ class SemiSpace : public Space { current_page_(NULL) {} // Sets up the semispace using the given chunk. - void SetUp(Address start, int initial_capacity, int maximum_capacity); + void SetUp(Address start, int initial_capacity, int target_capacity, + int maximum_capacity); // Tear down the space. Heap memory was not allocated by the space, so it // is not deallocated here. @@ -2094,6 +2095,9 @@ class SemiSpace : public Space { // semispace and less than the current capacity. bool ShrinkTo(int new_capacity); + // Sets the total capacity. Only possible when the space is not committed. + bool SetTotalCapacity(int new_capacity); + // Returns the start address of the first page of the space. Address space_start() { DCHECK(anchor_.next_page() != &anchor_); @@ -2168,6 +2172,9 @@ class SemiSpace : public Space { // Returns the current total capacity of the semispace. int TotalCapacity() { return total_capacity_; } + // Returns the target for total capacity of the semispace. + int TargetCapacity() { return target_capacity_; } + // Returns the maximum total capacity of the semispace. int MaximumTotalCapacity() { return maximum_total_capacity_; } @@ -2196,6 +2203,7 @@ class SemiSpace : public Space { // The current and maximum total capacity of the space. int total_capacity_; + int target_capacity_; int maximum_total_capacity_; int initial_total_capacity_; @@ -2341,6 +2349,9 @@ class NewSpace : public Space { // their maximum capacity. void Grow(); + // Grow the capacity of the semispaces by one page. + bool GrowOnePage(); + // Shrink the capacity of the semispaces. void Shrink(); diff --git a/test/cctest/cctest.h b/test/cctest/cctest.h index 4f07869..a8239d2 100644 --- a/test/cctest/cctest.h +++ b/test/cctest/cctest.h @@ -481,15 +481,31 @@ static inline void ExpectUndefined(const char* code) { // Helper function that simulates a full new-space in the heap. -static inline void SimulateFullSpace(v8::internal::NewSpace* space) { - int new_linear_size = static_cast( - *space->allocation_limit_address() - *space->allocation_top_address()); - if (new_linear_size == 0) return; +static inline bool FillUpOnePage(v8::internal::NewSpace* space) { v8::internal::AllocationResult allocation = - space->AllocateRaw(new_linear_size); + space->AllocateRaw(v8::internal::Page::kMaxRegularHeapObjectSize); + if (allocation.IsRetry()) return false; v8::internal::FreeListNode* node = v8::internal::FreeListNode::cast(allocation.ToObjectChecked()); - node->set_size(space->heap(), new_linear_size); + node->set_size(space->heap(), v8::internal::Page::kMaxRegularHeapObjectSize); + return true; +} + + +static inline void SimulateFullSpace(v8::internal::NewSpace* space) { + int new_linear_size = static_cast(*space->allocation_limit_address() - + *space->allocation_top_address()); + if (new_linear_size > 0) { + // Fill up the current page. + v8::internal::AllocationResult allocation = + space->AllocateRaw(new_linear_size); + v8::internal::FreeListNode* node = + v8::internal::FreeListNode::cast(allocation.ToObjectChecked()); + node->set_size(space->heap(), new_linear_size); + } + // Fill up all remaining pages. + while (FillUpOnePage(space)) + ; } diff --git a/test/cctest/test-spaces.cc b/test/cctest/test-spaces.cc index 5c01f5b..5667b20 100644 --- a/test/cctest/test-spaces.cc +++ b/test/cctest/test-spaces.cc @@ -456,3 +456,54 @@ TEST(SizeOfFirstPageIsLargeEnough) { // No large objects required to perform the above steps. CHECK(isolate->heap()->lo_space()->IsEmpty()); } + + +static inline void FillCurrentPage(v8::internal::NewSpace* space) { + int new_linear_size = static_cast(*space->allocation_limit_address() - + *space->allocation_top_address()); + if (new_linear_size == 0) return; + v8::internal::AllocationResult allocation = + space->AllocateRaw(new_linear_size); + v8::internal::FreeListNode* node = + v8::internal::FreeListNode::cast(allocation.ToObjectChecked()); + node->set_size(space->heap(), new_linear_size); +} + + +UNINITIALIZED_TEST(NewSpaceGrowsToTargetCapacity) { + FLAG_target_semi_space_size = 2; + + v8::Isolate* isolate = v8::Isolate::New(); + { + v8::Isolate::Scope isolate_scope(isolate); + v8::HandleScope handle_scope(isolate); + v8::Context::New(isolate)->Enter(); + + Isolate* i_isolate = reinterpret_cast(isolate); + + NewSpace* new_space = i_isolate->heap()->new_space(); + + // This test doesn't work if we start with a non-default new space + // configuration. + if (new_space->InitialTotalCapacity() == Page::kPageSize) { + CHECK(new_space->CommittedMemory() == new_space->InitialTotalCapacity()); + + // Fill up the first (and only) page of the semi space. + FillCurrentPage(new_space); + + // Try to allocate out of the new space. A new page should be added and + // the + // allocation should succeed. + v8::internal::AllocationResult allocation = new_space->AllocateRaw(80); + CHECK(!allocation.IsRetry()); + CHECK(new_space->CommittedMemory() == 2 * Page::kPageSize); + + // Turn the allocation into a proper object so isolate teardown won't + // crash. + v8::internal::FreeListNode* node = + v8::internal::FreeListNode::cast(allocation.ToObjectChecked()); + node->set_size(new_space->heap(), 80); + } + } + isolate->Dispose(); +}