Adapt fragmentation heuristics for over reserved pages.
authormstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 22 Jun 2012 09:29:49 +0000 (09:29 +0000)
committermstarzinger@chromium.org <mstarzinger@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 22 Jun 2012 09:29:49 +0000 (09:29 +0000)
This adapts the heuristics that detect fragmented pages to reduce memory
footprint for spaces with over reserved memory. This minimizes external
fragmentation caused by pages that cannot be released to the OS because
of just a few live objects on them.

R=erik.corry@gmail.com
TEST=cctest/test-heap/ReleaseOverReservedPages

Review URL: https://chromiumcodereview.appspot.com/10629004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11901 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/mark-compact.cc
test/cctest/test-alloc.cc
test/cctest/test-heap.cc

index 67f6e8e..97352e6 100644 (file)
@@ -500,12 +500,10 @@ void MarkCompactCollector::CollectEvacuationCandidates(PagedSpace* space) {
          space->identity() == OLD_DATA_SPACE ||
          space->identity() == CODE_SPACE);
 
+  static const int kMaxMaxEvacuationCandidates = 1000;
   int number_of_pages = space->CountTotalPages();
-
-  const int kMaxMaxEvacuationCandidates = 1000;
-  int max_evacuation_candidates = Min(
-    kMaxMaxEvacuationCandidates,
-    static_cast<int>(sqrt(static_cast<double>(number_of_pages / 2)) + 1));
+  int max_evacuation_candidates =
+      static_cast<int>(sqrt(static_cast<double>(number_of_pages / 2)) + 1);
 
   if (FLAG_stress_compaction || FLAG_always_compact) {
     max_evacuation_candidates = kMaxMaxEvacuationCandidates;
@@ -535,17 +533,28 @@ void MarkCompactCollector::CollectEvacuationCandidates(PagedSpace* space) {
   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;
+  if (over_reserved >= 2 * space->AreaSize()) {
 
-    // We expect that empty pages are easier to compact so slightly bump the
-    // limit.
-    max_evacuation_candidates += 2;
+    // If reduction of memory footprint was requested, we are aggressive
+    // about choosing pages to free.  We expect that half-empty pages
+    // are easier to compact so slightly bump the limit.
+    if (reduce_memory_footprint_) {
+      mode = REDUCE_MEMORY_FOOTPRINT;
+      max_evacuation_candidates += 2;
+    }
 
-    if (FLAG_trace_fragmentation) {
-      PrintF("Estimated over reserved memory: %.1f MB (setting threshold %d)\n",
+    // If over-usage is very high (more than a third of the space), we
+    // try to free all mostly empty pages.  We expect that almost empty
+    // pages are even easier to compact so bump the limit even more.
+    if (over_reserved > reserved / 3) {
+      mode = REDUCE_MEMORY_FOOTPRINT;
+      max_evacuation_candidates *= 2;
+    }
+
+    if (FLAG_trace_fragmentation && mode == REDUCE_MEMORY_FOOTPRINT) {
+      PrintF("Estimated over reserved memory: %.1f / %.1f MB (threshold %d)\n",
              static_cast<double>(over_reserved) / MB,
+             static_cast<double>(reserved) / MB,
              static_cast<int>(kFreenessThreshold));
     }
   }
@@ -554,6 +563,9 @@ void MarkCompactCollector::CollectEvacuationCandidates(PagedSpace* space) {
 
   Candidate candidates[kMaxMaxEvacuationCandidates];
 
+  max_evacuation_candidates =
+      Min(kMaxMaxEvacuationCandidates, max_evacuation_candidates);
+
   int count = 0;
   int fragmentation = 0;
   Candidate* least = NULL;
index e195d14..a8e504f 100644 (file)
@@ -34,7 +34,8 @@
 using namespace v8::internal;
 
 
-static inline void SimulateFullSpace(PagedSpace* space) {
+// Also used in test-heap.cc test cases.
+void SimulateFullSpace(PagedSpace* space) {
   int old_linear_size = static_cast<int>(space->limit() - space->top());
   space->Free(space->top(), old_linear_size);
   space->SetTop(space->limit(), space->limit());
index 179ca1c..e7ff3d0 100644 (file)
@@ -1899,3 +1899,42 @@ TEST(Regress2143b) {
   CHECK(root->IsJSObject());
   CHECK(root->map()->IsMap());
 }
+
+
+// Implemented in the test-alloc.cc test suite.
+void SimulateFullSpace(PagedSpace* space);
+
+
+TEST(ReleaseOverReservedPages) {
+  i::FLAG_trace_gc = true;
+  InitializeVM();
+  v8::HandleScope scope;
+  static const int number_of_test_pages = 20;
+
+  // Prepare many pages with low live-bytes count.
+  PagedSpace* old_pointer_space = HEAP->old_pointer_space();
+  CHECK_EQ(1, old_pointer_space->CountTotalPages());
+  for (int i = 0; i < number_of_test_pages; i++) {
+    AlwaysAllocateScope always_allocate;
+    SimulateFullSpace(old_pointer_space);
+    FACTORY->NewFixedArray(1, TENURED);
+  }
+  CHECK_EQ(number_of_test_pages + 1, old_pointer_space->CountTotalPages());
+
+  // Triggering one GC will cause a lot of garbage to be discovered but
+  // even spread across all allocated pages.
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags, "triggered for preparation");
+  CHECK_EQ(number_of_test_pages + 1, old_pointer_space->CountTotalPages());
+
+  // Triggering subsequent GCs should cause at least half of the pages
+  // to be released to the OS after at most two cycles.
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags, "triggered by test 1");
+  CHECK_GE(number_of_test_pages + 1, old_pointer_space->CountTotalPages());
+  HEAP->CollectAllGarbage(Heap::kNoGCFlags, "triggered by test 2");
+  CHECK_GE(number_of_test_pages + 1, old_pointer_space->CountTotalPages() * 2);
+
+  // Triggering a last-resort GC should cause all pages to be released
+  // to the OS so that other processes can seize the memory.
+  HEAP->CollectAllAvailableGarbage("triggered really hard");
+  CHECK_EQ(1, old_pointer_space->CountTotalPages());
+}