[heap] Remove border page

A page now belongs either the nursery *or* the intermediate gen. The page that
contained objects of both spaces is removed in this change.

BUG=chromium:636331

Review-Url: https://codereview.chromium.org/2209583002
Cr-Commit-Position: refs/heads/master@{#39778}
This commit is contained in:
mlippautz 2016-09-27 08:02:05 -07:00 committed by Commit bot
parent 0fb486fe44
commit 42ece47446
8 changed files with 109 additions and 94 deletions

View File

@ -519,9 +519,6 @@ bool Heap::OldGenerationAllocationLimitReached() {
template <PromotionMode promotion_mode> template <PromotionMode promotion_mode>
bool Heap::ShouldBePromoted(Address old_address, int object_size) { bool Heap::ShouldBePromoted(Address old_address, int object_size) {
Page* page = Page::FromAddress(old_address);
Address age_mark = new_space_->age_mark();
if (promotion_mode == PROMOTE_MARKED) { if (promotion_mode == PROMOTE_MARKED) {
MarkBit mark_bit = ObjectMarking::MarkBitFrom(old_address); MarkBit mark_bit = ObjectMarking::MarkBitFrom(old_address);
if (!Marking::IsWhite(mark_bit)) { if (!Marking::IsWhite(mark_bit)) {
@ -529,8 +526,7 @@ bool Heap::ShouldBePromoted(Address old_address, int object_size) {
} }
} }
return page->IsFlagSet(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK) && return Page::FromAddress(old_address)->InIntermediateGeneration();
(!page->ContainsLimit(age_mark) || old_address < age_mark);
} }
PromotionMode Heap::CurrentPromotionMode() { PromotionMode Heap::CurrentPromotionMode() {
@ -631,16 +627,8 @@ AllocationMemento* Heap::FindAllocationMemento(HeapObject* object) {
// Bail out if the memento is below the age mark, which can happen when // Bail out if the memento is below the age mark, which can happen when
// mementos survived because a page got moved within new space. // mementos survived because a page got moved within new space.
Page* object_page = Page::FromAddress(object_address); Page* object_page = Page::FromAddress(object_address);
if (object_page->IsFlagSet(Page::NEW_SPACE_BELOW_AGE_MARK)) { if (object_page->InIntermediateGeneration()) {
Address age_mark = return nullptr;
reinterpret_cast<SemiSpace*>(object_page->owner())->age_mark();
if (!object_page->Contains(age_mark)) {
return nullptr;
}
// Do an exact check in the case where the age mark is on the same page.
if (object_address < age_mark) {
return nullptr;
}
} }
AllocationMemento* memento_candidate = AllocationMemento::cast(candidate); AllocationMemento* memento_candidate = AllocationMemento::cast(candidate);

View File

@ -1755,8 +1755,7 @@ void Heap::Scavenge() {
DCHECK(new_space_front == new_space_->top()); DCHECK(new_space_front == new_space_->top());
// Set age mark. new_space_->SealIntermediateGeneration();
new_space_->set_age_mark(new_space_->top());
ArrayBufferTracker::FreeDeadInNewSpace(this); ArrayBufferTracker::FreeDeadInNewSpace(this);
@ -5107,7 +5106,6 @@ bool Heap::ConfigureHeap(int max_semi_space_size, int max_old_space_size,
} }
initial_semispace_size_ = Min(initial_semispace_size_, max_semi_space_size_); initial_semispace_size_ = Min(initial_semispace_size_, max_semi_space_size_);
if (FLAG_semi_space_growth_factor < 2) { if (FLAG_semi_space_growth_factor < 2) {
FLAG_semi_space_growth_factor = 2; FLAG_semi_space_growth_factor = 2;
} }

View File

@ -1364,7 +1364,6 @@ class Heap {
} }
inline void UpdateNewSpaceAllocationCounter(); inline void UpdateNewSpaceAllocationCounter();
inline size_t NewSpaceAllocationCounter(); inline size_t NewSpaceAllocationCounter();
// This should be used only for testing. // This should be used only for testing.

View File

@ -3188,15 +3188,13 @@ bool MarkCompactCollector::Evacuator::EvacuatePage(Page* page) {
if (FLAG_trace_evacuation) { if (FLAG_trace_evacuation) {
PrintIsolate(heap->isolate(), PrintIsolate(heap->isolate(),
"evacuation[%p]: page=%p new_space=%d " "evacuation[%p]: page=%p new_space=%d "
"page_evacuation=%d executable=%d contains_age_mark=%d " "page_evacuation=%d executable=%d live_bytes=%d time=%f\n",
"live_bytes=%d time=%f\n",
static_cast<void*>(this), static_cast<void*>(page), static_cast<void*>(this), static_cast<void*>(page),
page->InNewSpace(), page->InNewSpace(),
page->IsFlagSet(Page::PAGE_NEW_OLD_PROMOTION) || page->IsFlagSet(Page::PAGE_NEW_OLD_PROMOTION) ||
page->IsFlagSet(Page::PAGE_NEW_NEW_PROMOTION), page->IsFlagSet(Page::PAGE_NEW_NEW_PROMOTION),
page->IsFlagSet(MemoryChunk::IS_EXECUTABLE), page->IsFlagSet(MemoryChunk::IS_EXECUTABLE), saved_live_bytes,
page->Contains(heap->new_space()->age_mark()), evacuation_time);
saved_live_bytes, evacuation_time);
} }
return success; return success;
} }
@ -3306,13 +3304,11 @@ void MarkCompactCollector::EvacuatePagesInParallel() {
job.AddPage(page, &abandoned_pages); job.AddPage(page, &abandoned_pages);
} }
const Address age_mark = heap()->new_space()->age_mark();
for (Page* page : newspace_evacuation_candidates_) { for (Page* page : newspace_evacuation_candidates_) {
live_bytes += page->LiveBytes(); live_bytes += page->LiveBytes();
if (!page->NeverEvacuate() && if (!page->NeverEvacuate() &&
(page->LiveBytes() > Evacuator::PageEvacuationThreshold()) && (page->LiveBytes() > Evacuator::PageEvacuationThreshold())) {
!page->Contains(age_mark)) { if (page->InIntermediateGeneration()) {
if (page->IsFlagSet(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK)) {
EvacuateNewSpacePageVisitor::MoveToOldSpace(page, heap()->old_space()); EvacuateNewSpacePageVisitor::MoveToOldSpace(page, heap()->old_space());
} else { } else {
EvacuateNewSpacePageVisitor::MoveToToSpace(page); EvacuateNewSpacePageVisitor::MoveToToSpace(page);
@ -3558,7 +3554,7 @@ void MarkCompactCollector::EvacuateNewSpaceAndCandidates() {
EvacuateNewSpacePrologue(); EvacuateNewSpacePrologue();
EvacuatePagesInParallel(); EvacuatePagesInParallel();
heap()->new_space()->set_age_mark(heap()->new_space()->top()); heap()->new_space()->SealIntermediateGeneration();
} }
UpdatePointersAfterEvacuation(); UpdatePointersAfterEvacuation();

View File

@ -165,6 +165,22 @@ bool NewSpace::FromSpaceContainsSlow(Address a) {
bool NewSpace::ToSpaceContains(Object* o) { return to_space_.Contains(o); } bool NewSpace::ToSpaceContains(Object* o) { return to_space_.Contains(o); }
bool NewSpace::FromSpaceContains(Object* o) { return from_space_.Contains(o); } bool NewSpace::FromSpaceContains(Object* o) { return from_space_.Contains(o); }
size_t NewSpace::AllocatedSinceLastGC() {
Page* top_page = Page::FromAllocationAreaAddress(top());
size_t allocated = 0;
// If top gets reset to be in the range of pages that are below the age
// mark, this loop will not trigger and we return 0 (invalid).
for (Page* current_page = top_page;
!current_page->InIntermediateGeneration() &&
current_page != to_space_.anchor();
current_page = current_page->prev_page()) {
allocated += (top_page == current_page)
? static_cast<size_t>(top() - current_page->area_start())
: Page::kAllocatableMemory;
}
return allocated;
}
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// AllocationResult // AllocationResult

View File

@ -1485,8 +1485,9 @@ void PagedSpace::Verify(ObjectVisitor* visitor) {
bool NewSpace::SetUp(int initial_semispace_capacity, bool NewSpace::SetUp(int initial_semispace_capacity,
int maximum_semispace_capacity) { int maximum_semispace_capacity) {
DCHECK(initial_semispace_capacity <= maximum_semispace_capacity); DCHECK_LE(initial_semispace_capacity, maximum_semispace_capacity);
DCHECK(base::bits::IsPowerOfTwo32(maximum_semispace_capacity)); DCHECK(base::bits::IsPowerOfTwo32(maximum_semispace_capacity));
DCHECK_GE(initial_semispace_capacity, 2 * Page::kPageSize);
to_space_.SetUp(initial_semispace_capacity, maximum_semispace_capacity); to_space_.SetUp(initial_semispace_capacity, maximum_semispace_capacity);
from_space_.SetUp(initial_semispace_capacity, maximum_semispace_capacity); from_space_.SetUp(initial_semispace_capacity, maximum_semispace_capacity);
@ -1587,8 +1588,16 @@ bool SemiSpace::EnsureCurrentCapacity() {
current_page = current_page->next_page(); current_page = current_page->next_page();
if (actual_pages > expected_pages) { if (actual_pages > expected_pages) {
Page* to_remove = current_page->prev_page(); Page* to_remove = current_page->prev_page();
// Make sure we don't overtake the actual top pointer. if (to_remove == current_page_) {
CHECK_NE(to_remove, current_page_); // Corner case: All pages have been moved within new space. We are
// removing the page that contains the top pointer and need to set
// it to the end of the intermediate generation.
NewSpace* new_space = heap()->new_space();
CHECK_EQ(new_space->top(), current_page_->area_start());
current_page_ = to_remove->prev_page();
CHECK(current_page_->InIntermediateGeneration());
new_space->SetAllocationInfo(page_high(), page_high());
}
to_remove->Unlink(); to_remove->Unlink();
heap()->memory_allocator()->Free<MemoryAllocator::kPooledAndQueue>( heap()->memory_allocator()->Free<MemoryAllocator::kPooledAndQueue>(
to_remove); to_remove);
@ -1914,9 +1923,6 @@ bool SemiSpace::Commit() {
} }
Reset(); Reset();
AccountCommitted(current_capacity_); AccountCommitted(current_capacity_);
if (age_mark_ == nullptr) {
age_mark_ = first_page()->area_start();
}
committed_ = true; committed_ = true;
return true; return true;
} }
@ -2028,7 +2034,7 @@ void SemiSpace::FixPagesFlags(intptr_t flags, intptr_t mask) {
if (id_ == kToSpace) { if (id_ == kToSpace) {
page->ClearFlag(MemoryChunk::IN_FROM_SPACE); page->ClearFlag(MemoryChunk::IN_FROM_SPACE);
page->SetFlag(MemoryChunk::IN_TO_SPACE); page->SetFlag(MemoryChunk::IN_TO_SPACE);
page->ClearFlag(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK); page->ClearFlag(MemoryChunk::IN_INTERMEDIATE_GENERATION);
page->ResetLiveBytes(); page->ResetLiveBytes();
} else { } else {
page->SetFlag(MemoryChunk::IN_FROM_SPACE); page->SetFlag(MemoryChunk::IN_FROM_SPACE);
@ -2071,7 +2077,6 @@ void SemiSpace::Swap(SemiSpace* from, SemiSpace* to) {
std::swap(from->current_capacity_, to->current_capacity_); std::swap(from->current_capacity_, to->current_capacity_);
std::swap(from->maximum_capacity_, to->maximum_capacity_); std::swap(from->maximum_capacity_, to->maximum_capacity_);
std::swap(from->minimum_capacity_, to->minimum_capacity_); std::swap(from->minimum_capacity_, to->minimum_capacity_);
std::swap(from->age_mark_, to->age_mark_);
std::swap(from->committed_, to->committed_); std::swap(from->committed_, to->committed_);
std::swap(from->anchor_, to->anchor_); std::swap(from->anchor_, to->anchor_);
std::swap(from->current_page_, to->current_page_); std::swap(from->current_page_, to->current_page_);
@ -2080,17 +2085,40 @@ void SemiSpace::Swap(SemiSpace* from, SemiSpace* to) {
from->FixPagesFlags(0, 0); from->FixPagesFlags(0, 0);
} }
void NewSpace::SealIntermediateGeneration() {
fragmentation_in_intermediate_generation_ = 0;
const Address mark = top();
void SemiSpace::set_age_mark(Address mark) { if (mark == to_space_.space_start()) {
DCHECK_EQ(Page::FromAllocationAreaAddress(mark)->owner(), this); // Do not mark any pages as being part of the intermediate generation if no
age_mark_ = mark; // objects got moved.
// Mark all pages up to the one containing mark. return;
for (Page* p : NewSpacePageRange(space_start(), mark)) { }
p->SetFlag(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK);
for (Page* p : NewSpacePageRange(to_space_.space_start(), mark)) {
p->SetFlag(MemoryChunk::IN_INTERMEDIATE_GENERATION);
}
Page* p = Page::FromAllocationAreaAddress(mark);
if (mark < p->area_end()) {
heap()->CreateFillerObjectAt(mark, static_cast<int>(p->area_end() - mark),
ClearRecordedSlots::kNo);
fragmentation_in_intermediate_generation_ =
static_cast<size_t>(p->area_end() - mark);
DCHECK_EQ(to_space_.current_page(), p);
if (to_space_.AdvancePage()) {
UpdateAllocationInfo();
} else {
allocation_info_.Reset(to_space_.page_high(), to_space_.page_high());
}
}
if (FLAG_trace_gc_verbose) {
PrintIsolate(heap()->isolate(),
"Sealing intermediate generation: bytes_lost=%zu\n",
fragmentation_in_intermediate_generation_);
} }
} }
#ifdef DEBUG #ifdef DEBUG
void SemiSpace::Print() {} void SemiSpace::Print() {}
#endif #endif

View File

@ -234,10 +234,14 @@ class MemoryChunk {
IS_EXECUTABLE = 1u << 0, IS_EXECUTABLE = 1u << 0,
POINTERS_TO_HERE_ARE_INTERESTING = 1u << 1, POINTERS_TO_HERE_ARE_INTERESTING = 1u << 1,
POINTERS_FROM_HERE_ARE_INTERESTING = 1u << 2, POINTERS_FROM_HERE_ARE_INTERESTING = 1u << 2,
// A page in new space has one of the next to flags set. // A page in new space has one of the next to flags set.
IN_FROM_SPACE = 1u << 3, IN_FROM_SPACE = 1u << 3,
IN_TO_SPACE = 1u << 4, IN_TO_SPACE = 1u << 4,
NEW_SPACE_BELOW_AGE_MARK = 1u << 5, // |IN_INTERMEDIATE_GENERATION|: Flag indicates whether this page contains
// objects that have already been copied once.
IN_INTERMEDIATE_GENERATION = 1u << 5,
EVACUATION_CANDIDATE = 1u << 6, EVACUATION_CANDIDATE = 1u << 6,
NEVER_EVACUATE = 1u << 7, NEVER_EVACUATE = 1u << 7,
@ -559,6 +563,10 @@ class MemoryChunk {
bool InFromSpace() { return IsFlagSet(IN_FROM_SPACE); } bool InFromSpace() { return IsFlagSet(IN_FROM_SPACE); }
bool InIntermediateGeneration() {
return IsFlagSet(IN_INTERMEDIATE_GENERATION);
}
MemoryChunk* next_chunk() { return next_chunk_.Value(); } MemoryChunk* next_chunk() { return next_chunk_.Value(); }
MemoryChunk* prev_chunk() { return prev_chunk_.Value(); } MemoryChunk* prev_chunk() { return prev_chunk_.Value(); }
@ -2224,7 +2232,6 @@ class SemiSpace : public Space {
current_capacity_(0), current_capacity_(0),
maximum_capacity_(0), maximum_capacity_(0),
minimum_capacity_(0), minimum_capacity_(0),
age_mark_(nullptr),
committed_(false), committed_(false),
id_(semispace), id_(semispace),
anchor_(this), anchor_(this),
@ -2293,10 +2300,6 @@ class SemiSpace : public Space {
void RemovePage(Page* page); void RemovePage(Page* page);
void PrependPage(Page* page); void PrependPage(Page* page);
// Age mark accessors.
Address age_mark() { return age_mark_; }
void set_age_mark(Address mark);
// Returns the current capacity of the semispace. // Returns the current capacity of the semispace.
int current_capacity() { return current_capacity_; } int current_capacity() { return current_capacity_; }
@ -2363,9 +2366,6 @@ class SemiSpace : public Space {
// The minimum capacity for the space. A space cannot shrink below this size. // The minimum capacity for the space. A space cannot shrink below this size.
int minimum_capacity_; int minimum_capacity_;
// Used to govern object promotion during mark-compact collection.
Address age_mark_;
bool committed_; bool committed_;
SemiSpaceId id_; SemiSpaceId id_;
@ -2416,7 +2416,8 @@ class NewSpace : public Space {
reservation_(), reservation_(),
top_on_previous_step_(0), top_on_previous_step_(0),
allocated_histogram_(nullptr), allocated_histogram_(nullptr),
promoted_histogram_(nullptr) {} promoted_histogram_(nullptr),
fragmentation_in_intermediate_generation_(0) {}
inline bool Contains(HeapObject* o); inline bool Contains(HeapObject* o);
inline bool ContainsSlow(Address a); inline bool ContainsSlow(Address a);
@ -2449,6 +2450,11 @@ class NewSpace : public Space {
static_cast<int>(top() - to_space_.page_low()); static_cast<int>(top() - to_space_.page_low());
} }
intptr_t SizeOfObjects() override {
return Size() -
static_cast<intptr_t>(fragmentation_in_intermediate_generation_);
}
// The same, but returning an int. We have to have the one that returns // The same, but returning an int. We have to have the one that returns
// intptr_t because it is inherited, but if we know we are dealing with the // intptr_t because it is inherited, but if we know we are dealing with the
// new space, which can't get as big as the other spaces then this is useful: // new space, which can't get as big as the other spaces then this is useful:
@ -2485,42 +2491,7 @@ class NewSpace : public Space {
// Return the available bytes without growing. // Return the available bytes without growing.
intptr_t Available() override { return Capacity() - Size(); } intptr_t Available() override { return Capacity() - Size(); }
size_t AllocatedSinceLastGC() { inline size_t AllocatedSinceLastGC();
bool seen_age_mark = false;
Address age_mark = to_space_.age_mark();
Page* current_page = to_space_.first_page();
Page* age_mark_page = Page::FromAddress(age_mark);
Page* last_page = Page::FromAddress(top() - kPointerSize);
if (age_mark_page == last_page) {
if (top() - age_mark >= 0) {
return top() - age_mark;
}
// Top was reset at some point, invalidating this metric.
return 0;
}
while (current_page != last_page) {
if (current_page == age_mark_page) {
seen_age_mark = true;
break;
}
current_page = current_page->next_page();
}
if (!seen_age_mark) {
// Top was reset at some point, invalidating this metric.
return 0;
}
intptr_t allocated = age_mark_page->area_end() - age_mark;
DCHECK_EQ(current_page, age_mark_page);
current_page = age_mark_page->next_page();
while (current_page != last_page) {
allocated += Page::kAllocatableMemory;
current_page = current_page->next_page();
}
allocated += top() - current_page->area_start();
DCHECK_LE(0, allocated);
DCHECK_LE(allocated, Size());
return static_cast<size_t>(allocated);
}
void MovePageFromSpaceToSpace(Page* page) { void MovePageFromSpaceToSpace(Page* page) {
DCHECK(page->InFromSpace()); DCHECK(page->InFromSpace());
@ -2559,10 +2530,8 @@ class NewSpace : public Space {
// Return the address of the first object in the active semispace. // Return the address of the first object in the active semispace.
Address bottom() { return to_space_.space_start(); } Address bottom() { return to_space_.space_start(); }
// Get the age mark of the inactive semispace. // Seal the intermediate generation of the active semispace.
Address age_mark() { return from_space_.age_mark(); } void SealIntermediateGeneration();
// Set the age mark in the active semispace.
void set_age_mark(Address mark) { to_space_.set_age_mark(mark); }
// The allocation top and limit address. // The allocation top and limit address.
Address* allocation_top_address() { return allocation_info_.top_address(); } Address* allocation_top_address() { return allocation_info_.top_address(); }
@ -2587,6 +2556,10 @@ class NewSpace : public Space {
// Reset the allocation pointer to the beginning of the active semispace. // Reset the allocation pointer to the beginning of the active semispace.
void ResetAllocationInfo(); void ResetAllocationInfo();
void SetAllocationInfo(Address top, Address limit) {
allocation_info_.Reset(top, limit);
}
// When inline allocation stepping is active, either because of incremental // When inline allocation stepping is active, either because of incremental
// marking, idle scavenge, or allocation statistics gathering, we 'interrupt' // marking, idle scavenge, or allocation statistics gathering, we 'interrupt'
// inline allocation every once in a while. This is done by setting // inline allocation every once in a while. This is done by setting
@ -2695,6 +2668,8 @@ class NewSpace : public Space {
HistogramInfo* allocated_histogram_; HistogramInfo* allocated_histogram_;
HistogramInfo* promoted_histogram_; HistogramInfo* promoted_histogram_;
size_t fragmentation_in_intermediate_generation_;
bool EnsureAllocation(int size_in_bytes, AllocationAlignment alignment); bool EnsureAllocation(int size_in_bytes, AllocationAlignment alignment);
// If we are doing inline allocation in steps, this method performs the 'step' // If we are doing inline allocation in steps, this method performs the 'step'

View File

@ -7110,5 +7110,20 @@ TEST(RememberedSetRemoveRange) {
}); });
} }
TEST(EmptyIntermediateGeneration) {
CcTest::InitializeVM();
Heap* heap = CcTest::heap();
heap::GcAndSweep(heap, OLD_SPACE);
v8::HandleScope scope(CcTest::isolate());
{
v8::HandleScope temp_scope(CcTest::isolate());
heap::SimulateFullSpace(heap->new_space());
}
heap::GcAndSweep(heap, OLD_SPACE);
for (Page* p : *heap->new_space()) {
CHECK(!p->InIntermediateGeneration());
}
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8