Add support for a target new space size
authorjochen@chromium.org <jochen@chromium.org>
Wed, 22 Oct 2014 06:58:38 +0000 (06:58 +0000)
committerjochen@chromium.org <jochen@chromium.org>
Wed, 22 Oct 2014 06:58:38 +0000 (06:58 +0000)
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

src/flag-definitions.h
src/heap/heap.cc
src/heap/heap.h
src/heap/spaces.cc
src/heap/spaces.h
test/cctest/cctest.h
test/cctest/test-spaces.cc

index f5fdb8e..fc38ba4 100644 (file)
@@ -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")
index 2c7fc58..6e566c0 100644 (file)
@@ -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_ =
index 695ad8a..c0848c9 100644 (file)
@@ -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_;
index 62f6d62..85281aa 100644 (file)
@@ -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<int>(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
index ef55357..5be9c3a 100644 (file)
@@ -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();
 
index 4f07869..a8239d2 100644 (file)
@@ -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<int>(
-      *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<int>(*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))
+    ;
 }
 
 
index 5c01f5b..5667b20 100644 (file)
@@ -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<int>(*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*>(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();
+}