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 67f6e8e0a7556c89a1f5162ac441ec3da5b4a1c4..97352e69beeef5eb2e6a12dc1f30536cdaef40ae 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 e195d14923ef324dea976cd94aa2ec3d52594196..a8e504fd443a6c77dddfbde352c372f11206a569 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 179ca1c63cd1f2add33dfeeaf10fe8081cc89e80..e7ff3d0b26e28aac94b15266921e5d151ff1d860 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());
+}