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")
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
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;
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_ =
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_; }
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_;
// 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_);
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;
}
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);
}
}
}
}
+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);
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);
}
}
}
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.
// -----------------------------------------------------------------------------
// 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
// 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;
}
+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
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.
// 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_);
// 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_; }
// The current and maximum total capacity of the space.
int total_capacity_;
+ int target_capacity_;
int maximum_total_capacity_;
int initial_total_capacity_;
// their maximum capacity.
void Grow();
+ // Grow the capacity of the semispaces by one page.
+ bool GrowOnePage();
+
// Shrink the capacity of the semispaces.
void Shrink();
// 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))
+ ;
}
// 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();
+}