Version 4.10.140.1 (cherry-pick)
Merged a9441b0e7a2a
Revert of [heap] Simplify distribution of remaining memory during sweeping & compaction (patchset #2 id:80001 of https://codereview.chromium.org/1653973003/ )
BUG=chromium:524425
LOG=N
R=machenbach@chromium.org
Review URL: https://codereview.chromium.org/1664703002 .
Cr-Commit-Position: refs/heads/4.10.140@{#2}
Cr-Branched-From: 6b2001b6b7c7152c566004e78aa523e1c9d5c82a-refs/heads/master@{#33675}
diff --git a/include/v8-version.h b/include/v8-version.h
index 81b5e97..ee29baa 100644
--- a/include/v8-version.h
+++ b/include/v8-version.h
@@ -11,7 +11,7 @@
#define V8_MAJOR_VERSION 4
#define V8_MINOR_VERSION 10
#define V8_BUILD_NUMBER 140
-#define V8_PATCH_LEVEL 0
+#define V8_PATCH_LEVEL 1
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/heap/mark-compact.cc b/src/heap/mark-compact.cc
index f23918c..fa55649 100644
--- a/src/heap/mark-compact.cc
+++ b/src/heap/mark-compact.cc
@@ -3282,10 +3282,18 @@
// Set up compaction spaces.
Evacuator** evacuators = new Evacuator*[num_tasks];
+ CompactionSpaceCollection** compaction_spaces_for_tasks =
+ new CompactionSpaceCollection*[num_tasks];
for (int i = 0; i < num_tasks; i++) {
evacuators[i] = new Evacuator(this, evacuation_candidates_,
newspace_evacuation_candidates_);
+ compaction_spaces_for_tasks[i] = evacuators[i]->compaction_spaces();
}
+ heap()->old_space()->DivideUponCompactionSpaces(compaction_spaces_for_tasks,
+ num_tasks);
+ heap()->code_space()->DivideUponCompactionSpaces(compaction_spaces_for_tasks,
+ num_tasks);
+ delete[] compaction_spaces_for_tasks;
// Kick off parallel tasks.
StartParallelCompaction(evacuators, num_tasks);
diff --git a/src/heap/spaces.cc b/src/heap/spaces.cc
index 4d726ee..31354cc 100644
--- a/src/heap/spaces.cc
+++ b/src/heap/spaces.cc
@@ -984,6 +984,52 @@
}
+FreeSpace* PagedSpace::TryRemoveMemory(intptr_t size_in_bytes) {
+ FreeSpace* free_space = free_list()->TryRemoveMemory(size_in_bytes);
+ if (free_space != nullptr) {
+ accounting_stats_.DecreaseCapacity(free_space->size());
+ }
+ return free_space;
+}
+
+
+void PagedSpace::DivideUponCompactionSpaces(CompactionSpaceCollection** other,
+ int num, intptr_t limit) {
+ DCHECK_GT(num, 0);
+ DCHECK(other != nullptr);
+
+ if (limit == 0) limit = std::numeric_limits<intptr_t>::max();
+
+ EmptyAllocationInfo();
+
+ bool memory_available = true;
+ bool spaces_need_memory = true;
+ FreeSpace* node = nullptr;
+ CompactionSpace* current_space = nullptr;
+ // Iterate over spaces and memory as long as we have memory and there are
+ // spaces in need of some.
+ while (memory_available && spaces_need_memory) {
+ spaces_need_memory = false;
+ // Round-robin over all spaces.
+ for (int i = 0; i < num; i++) {
+ current_space = other[i]->Get(identity());
+ if (current_space->free_list()->Available() < limit) {
+ // Space has not reached its limit. Try to get some memory.
+ spaces_need_memory = true;
+ node = TryRemoveMemory(limit - current_space->free_list()->Available());
+ if (node != nullptr) {
+ CHECK(current_space->identity() == identity());
+ current_space->AddMemory(node->address(), node->size());
+ } else {
+ memory_available = false;
+ break;
+ }
+ }
+ }
+ }
+}
+
+
void PagedSpace::RefillFreeList() {
MarkCompactCollector* collector = heap()->mark_compact_collector();
FreeList* free_list = nullptr;
@@ -1026,6 +1072,7 @@
}
}
+
void PagedSpace::MoveOverFreeMemory(PagedSpace* other) {
DCHECK(identity() == other->identity());
// Destroy the linear allocation space of {other}. This is needed to
diff --git a/src/heap/spaces.h b/src/heap/spaces.h
index e233b32..b7aad77 100644
--- a/src/heap/spaces.h
+++ b/src/heap/spaces.h
@@ -2119,6 +2119,9 @@
// e.g., removes its bump pointer area and resets statistics.
void MergeCompactionSpace(CompactionSpace* other);
+ void DivideUponCompactionSpaces(CompactionSpaceCollection** other, int num,
+ intptr_t limit = kCompactionMemoryWanted);
+
// Refills the free list from the corresponding free list filled by the
// sweeper.
virtual void RefillFreeList();
@@ -2126,6 +2129,8 @@
protected:
void AddMemory(Address start, intptr_t size);
+ FreeSpace* TryRemoveMemory(intptr_t size_in_bytes);
+
void MoveOverFreeMemory(PagedSpace* other);
// PagedSpaces that should be included in snapshots have different, i.e.,
@@ -2888,6 +2893,12 @@
CompactionSpace(Heap* heap, AllocationSpace id, Executability executable)
: PagedSpace(heap, id, executable) {}
+ // Adds external memory starting at {start} of {size_in_bytes} to the space.
+ void AddExternalMemory(Address start, int size_in_bytes) {
+ IncreaseCapacity(size_in_bytes);
+ Free(start, size_in_bytes);
+ }
+
bool is_local() override { return true; }
void RefillFreeList() override;
diff --git a/test/cctest/heap/heap-tester.h b/test/cctest/heap/heap-tester.h
index 5175a5d..0a0860bc 100644
--- a/test/cctest/heap/heap-tester.h
+++ b/test/cctest/heap/heap-tester.h
@@ -59,6 +59,25 @@
/* test-api.cc */
static void ResetWeakHandle(bool global_gc);
+
+ /* test-spaces.cc */
+ static CompactionSpaceCollection** InitializeCompactionSpaces(Heap* heap,
+ int num_spaces);
+ static void DestroyCompactionSpaces(CompactionSpaceCollection** spaces,
+ int num_spaces);
+ static void MergeCompactionSpaces(PagedSpace* space,
+ CompactionSpaceCollection** spaces,
+ int num_spaces);
+ static void AllocateInCompactionSpaces(CompactionSpaceCollection** spaces,
+ AllocationSpace id, int num_spaces,
+ int num_objects, int object_size);
+ static void CompactionStats(CompactionSpaceCollection** spaces,
+ AllocationSpace id, int num_spaces,
+ intptr_t* capacity, intptr_t* size);
+ static void TestCompactionSpaceDivide(int num_additional_objects,
+ int object_size,
+ int num_compaction_spaces,
+ int additional_capacity_in_bytes);
};
} // namespace internal
diff --git a/test/cctest/heap/test-spaces.cc b/test/cctest/heap/test-spaces.cc
index 92224ca..2698411 100644
--- a/test/cctest/heap/test-spaces.cc
+++ b/test/cctest/heap/test-spaces.cc
@@ -448,6 +448,236 @@
}
+TEST(CompactionSpaceUsingExternalMemory) {
+ const int kObjectSize = 512;
+
+ Isolate* isolate = CcTest::i_isolate();
+ Heap* heap = isolate->heap();
+ MemoryAllocator* allocator = new MemoryAllocator(isolate);
+ CHECK(allocator != nullptr);
+ CHECK(allocator->SetUp(heap->MaxReserved(), heap->MaxExecutableSize()));
+ TestMemoryAllocatorScope test_scope(isolate, allocator);
+
+ CompactionSpaceCollection* collection = new CompactionSpaceCollection(heap);
+ CompactionSpace* compaction_space = collection->Get(OLD_SPACE);
+ CHECK(compaction_space != NULL);
+ CHECK(compaction_space->SetUp());
+
+ OldSpace* old_space = new OldSpace(heap, OLD_SPACE, NOT_EXECUTABLE);
+ CHECK(old_space != NULL);
+ CHECK(old_space->SetUp());
+
+ // The linear allocation area already counts as used bytes, making
+ // exact testing impossible.
+ heap->DisableInlineAllocation();
+
+ // Test:
+ // * Allocate a backing store in old_space.
+ // * Compute the number num_rest_objects of kObjectSize objects that fit into
+ // of available memory.
+ // kNumRestObjects.
+ // * Add the rest of available memory to the compaction space.
+ // * Allocate kNumRestObjects in the compaction space.
+ // * Allocate one object more.
+ // * Merge the compaction space and compare the expected number of pages.
+
+ // Allocate a single object in old_space to initialize a backing page.
+ old_space->AllocateRawUnaligned(kObjectSize).ToObjectChecked();
+ // Compute the number of objects that fit into the rest in old_space.
+ intptr_t rest = static_cast<int>(old_space->Available());
+ CHECK_GT(rest, 0);
+ intptr_t num_rest_objects = rest / kObjectSize;
+ // After allocating num_rest_objects in compaction_space we allocate a bit
+ // more.
+ const intptr_t kAdditionalCompactionMemory = kObjectSize;
+ // We expect a single old_space page.
+ const intptr_t kExpectedInitialOldSpacePages = 1;
+ // We expect a single additional page in compaction space because we mostly
+ // use external memory.
+ const intptr_t kExpectedCompactionPages = 1;
+ // We expect two pages to be reachable from old_space in the end.
+ const intptr_t kExpectedOldSpacePagesAfterMerge = 2;
+
+ CHECK_EQ(old_space->CountTotalPages(), kExpectedInitialOldSpacePages);
+ CHECK_EQ(compaction_space->CountTotalPages(), 0);
+ CHECK_EQ(compaction_space->Capacity(), 0);
+ // Make the rest of memory available for compaction.
+ old_space->DivideUponCompactionSpaces(&collection, 1, rest);
+ CHECK_EQ(compaction_space->CountTotalPages(), 0);
+ CHECK_EQ(compaction_space->Capacity(), rest);
+ while (num_rest_objects-- > 0) {
+ compaction_space->AllocateRawUnaligned(kObjectSize).ToObjectChecked();
+ }
+ // We only used external memory so far.
+ CHECK_EQ(compaction_space->CountTotalPages(), 0);
+ // Additional allocation.
+ compaction_space->AllocateRawUnaligned(kAdditionalCompactionMemory)
+ .ToObjectChecked();
+ // Now the compaction space shouldve also acquired a page.
+ CHECK_EQ(compaction_space->CountTotalPages(), kExpectedCompactionPages);
+
+ old_space->MergeCompactionSpace(compaction_space);
+ CHECK_EQ(old_space->CountTotalPages(), kExpectedOldSpacePagesAfterMerge);
+
+ delete collection;
+ delete old_space;
+
+ allocator->TearDown();
+ delete allocator;
+}
+
+
+CompactionSpaceCollection** HeapTester::InitializeCompactionSpaces(
+ Heap* heap, int num_spaces) {
+ CompactionSpaceCollection** spaces =
+ new CompactionSpaceCollection*[num_spaces];
+ for (int i = 0; i < num_spaces; i++) {
+ spaces[i] = new CompactionSpaceCollection(heap);
+ }
+ return spaces;
+}
+
+
+void HeapTester::DestroyCompactionSpaces(CompactionSpaceCollection** spaces,
+ int num_spaces) {
+ for (int i = 0; i < num_spaces; i++) {
+ delete spaces[i];
+ }
+ delete[] spaces;
+}
+
+
+void HeapTester::MergeCompactionSpaces(PagedSpace* space,
+ CompactionSpaceCollection** spaces,
+ int num_spaces) {
+ AllocationSpace id = space->identity();
+ for (int i = 0; i < num_spaces; i++) {
+ space->MergeCompactionSpace(spaces[i]->Get(id));
+ CHECK_EQ(spaces[i]->Get(id)->accounting_stats_.Size(), 0);
+ CHECK_EQ(spaces[i]->Get(id)->accounting_stats_.Capacity(), 0);
+ CHECK_EQ(spaces[i]->Get(id)->Waste(), 0);
+ }
+}
+
+
+void HeapTester::AllocateInCompactionSpaces(CompactionSpaceCollection** spaces,
+ AllocationSpace id, int num_spaces,
+ int num_objects, int object_size) {
+ for (int i = 0; i < num_spaces; i++) {
+ for (int j = 0; j < num_objects; j++) {
+ spaces[i]->Get(id)->AllocateRawUnaligned(object_size).ToObjectChecked();
+ }
+ spaces[i]->Get(id)->EmptyAllocationInfo();
+ CHECK_EQ(spaces[i]->Get(id)->accounting_stats_.Size(),
+ num_objects * object_size);
+ CHECK_GE(spaces[i]->Get(id)->accounting_stats_.Capacity(),
+ spaces[i]->Get(id)->accounting_stats_.Size());
+ }
+}
+
+
+void HeapTester::CompactionStats(CompactionSpaceCollection** spaces,
+ AllocationSpace id, int num_spaces,
+ intptr_t* capacity, intptr_t* size) {
+ *capacity = 0;
+ *size = 0;
+ for (int i = 0; i < num_spaces; i++) {
+ *capacity += spaces[i]->Get(id)->accounting_stats_.Capacity();
+ *size += spaces[i]->Get(id)->accounting_stats_.Size();
+ }
+}
+
+
+void HeapTester::TestCompactionSpaceDivide(int num_additional_objects,
+ int object_size,
+ int num_compaction_spaces,
+ int additional_capacity_in_bytes) {
+ Isolate* isolate = CcTest::i_isolate();
+ Heap* heap = isolate->heap();
+ OldSpace* old_space = new OldSpace(heap, OLD_SPACE, NOT_EXECUTABLE);
+ CHECK(old_space != nullptr);
+ CHECK(old_space->SetUp());
+ old_space->AllocateRawUnaligned(object_size).ToObjectChecked();
+ old_space->EmptyAllocationInfo();
+
+ intptr_t rest_capacity = old_space->accounting_stats_.Capacity() -
+ old_space->accounting_stats_.Size();
+ intptr_t capacity_for_compaction_space =
+ rest_capacity / num_compaction_spaces;
+ int num_objects_in_compaction_space =
+ static_cast<int>(capacity_for_compaction_space) / object_size +
+ num_additional_objects;
+ CHECK_GT(num_objects_in_compaction_space, 0);
+ intptr_t initial_old_space_capacity = old_space->accounting_stats_.Capacity();
+
+ CompactionSpaceCollection** spaces =
+ InitializeCompactionSpaces(heap, num_compaction_spaces);
+ old_space->DivideUponCompactionSpaces(spaces, num_compaction_spaces,
+ capacity_for_compaction_space);
+
+ intptr_t compaction_capacity = 0;
+ intptr_t compaction_size = 0;
+ CompactionStats(spaces, OLD_SPACE, num_compaction_spaces,
+ &compaction_capacity, &compaction_size);
+
+ intptr_t old_space_capacity = old_space->accounting_stats_.Capacity();
+ intptr_t old_space_size = old_space->accounting_stats_.Size();
+ // Compaction space memory is subtracted from the original space's capacity.
+ CHECK_EQ(old_space_capacity,
+ initial_old_space_capacity - compaction_capacity);
+ CHECK_EQ(compaction_size, 0);
+
+ AllocateInCompactionSpaces(spaces, OLD_SPACE, num_compaction_spaces,
+ num_objects_in_compaction_space, object_size);
+
+ // Old space size and capacity should be the same as after dividing.
+ CHECK_EQ(old_space->accounting_stats_.Size(), old_space_size);
+ CHECK_EQ(old_space->accounting_stats_.Capacity(), old_space_capacity);
+
+ CompactionStats(spaces, OLD_SPACE, num_compaction_spaces,
+ &compaction_capacity, &compaction_size);
+ MergeCompactionSpaces(old_space, spaces, num_compaction_spaces);
+
+ CHECK_EQ(old_space->accounting_stats_.Capacity(),
+ old_space_capacity + compaction_capacity);
+ CHECK_EQ(old_space->accounting_stats_.Size(),
+ old_space_size + compaction_size);
+ // We check against the expected end capacity.
+ CHECK_EQ(old_space->accounting_stats_.Capacity(),
+ initial_old_space_capacity + additional_capacity_in_bytes);
+
+ DestroyCompactionSpaces(spaces, num_compaction_spaces);
+ delete old_space;
+}
+
+
+HEAP_TEST(CompactionSpaceDivideSinglePage) {
+ const int kObjectSize = KB;
+ const int kCompactionSpaces = 4;
+ // Since the bound for objects is tight and the dividing is best effort, we
+ // subtract some objects to make sure we still fit in the initial page.
+ // A CHECK makes sure that the overall number of allocated objects stays
+ // > 0.
+ const int kAdditionalObjects = -10;
+ const int kAdditionalCapacityRequired = 0;
+ TestCompactionSpaceDivide(kAdditionalObjects, kObjectSize, kCompactionSpaces,
+ kAdditionalCapacityRequired);
+}
+
+
+HEAP_TEST(CompactionSpaceDivideMultiplePages) {
+ const int kObjectSize = KB;
+ const int kCompactionSpaces = 4;
+ // Allocate half a page of objects to ensure that we need one more page per
+ // compaction space.
+ const int kAdditionalObjects = (Page::kPageSize / kObjectSize / 2);
+ const int kAdditionalCapacityRequired =
+ Page::kAllocatableMemory * kCompactionSpaces;
+ TestCompactionSpaceDivide(kAdditionalObjects, kObjectSize, kCompactionSpaces,
+ kAdditionalCapacityRequired);
+}
+
+
TEST(LargeObjectSpace) {
v8::V8::Initialize();