Ensure that promotion queue does not overlap with objects relocated to ToSpace.

R=erik.corry@gmail.com

Review URL: http://codereview.chromium.org/8477030

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@9932 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
vegorov@chromium.org 2011-11-09 13:48:43 +00:00
parent 4391aff4a3
commit 9f50147031
6 changed files with 168 additions and 42 deletions

View File

@ -40,12 +40,30 @@ namespace v8 {
namespace internal { namespace internal {
void PromotionQueue::insert(HeapObject* target, int size) { void PromotionQueue::insert(HeapObject* target, int size) {
if (emergency_stack_ != NULL) {
emergency_stack_->Add(Entry(target, size));
return;
}
if (NewSpacePage::IsAtStart(reinterpret_cast<Address>(rear_))) { if (NewSpacePage::IsAtStart(reinterpret_cast<Address>(rear_))) {
NewSpacePage* rear_page = NewSpacePage* rear_page =
NewSpacePage::FromAddress(reinterpret_cast<Address>(rear_)); NewSpacePage::FromAddress(reinterpret_cast<Address>(rear_));
ASSERT(!rear_page->prev_page()->is_anchor()); ASSERT(!rear_page->prev_page()->is_anchor());
rear_ = reinterpret_cast<intptr_t*>(rear_page->prev_page()->body_limit()); rear_ = reinterpret_cast<intptr_t*>(rear_page->prev_page()->body_limit());
ActivateGuardIfOnTheSamePage();
} }
if (guard_) {
ASSERT(GetHeadPage() ==
Page::FromAllocationTop(reinterpret_cast<Address>(limit_)));
if ((rear_ - 2) < limit_) {
RelocateQueueHead();
emergency_stack_->Add(Entry(target, size));
return;
}
}
*(--rear_) = reinterpret_cast<intptr_t>(target); *(--rear_) = reinterpret_cast<intptr_t>(target);
*(--rear_) = size; *(--rear_) = size;
// Assert no overflow into live objects. // Assert no overflow into live objects.
@ -56,6 +74,13 @@ void PromotionQueue::insert(HeapObject* target, int size) {
} }
void PromotionQueue::ActivateGuardIfOnTheSamePage() {
guard_ = guard_ ||
heap_->new_space()->active_space()->current_page()->address() ==
GetHeadPage()->address();
}
int Heap::MaxObjectSizeInPagedSpace() { int Heap::MaxObjectSizeInPagedSpace() {
return Page::kMaxHeapObjectSize; return Page::kMaxHeapObjectSize;
} }

View File

@ -143,6 +143,7 @@ Heap::Heap()
number_idle_notifications_(0), number_idle_notifications_(0),
last_idle_notification_gc_count_(0), last_idle_notification_gc_count_(0),
last_idle_notification_gc_count_init_(false), last_idle_notification_gc_count_init_(false),
promotion_queue_(this),
configured_(false), configured_(false),
chunks_queued_for_free_(NULL) { chunks_queued_for_free_(NULL) {
// Allow build-time customization of the max semispace size. Building // Allow build-time customization of the max semispace size. Building
@ -988,6 +989,41 @@ void StoreBufferRebuilder::Callback(MemoryChunk* page, StoreBufferEvent event) {
} }
void PromotionQueue::Initialize() {
// Assumes that a NewSpacePage exactly fits a number of promotion queue
// entries (where each is a pair of intptr_t). This allows us to simplify
// the test fpr when to switch pages.
ASSERT((Page::kPageSize - MemoryChunk::kBodyOffset) % (2 * kPointerSize)
== 0);
limit_ = reinterpret_cast<intptr_t*>(heap_->new_space()->ToSpaceStart());
front_ = rear_ =
reinterpret_cast<intptr_t*>(heap_->new_space()->ToSpaceEnd());
emergency_stack_ = NULL;
guard_ = false;
}
void PromotionQueue::RelocateQueueHead() {
ASSERT(emergency_stack_ == NULL);
Page* p = Page::FromAllocationTop(reinterpret_cast<Address>(rear_));
intptr_t* head_start = rear_;
intptr_t* head_end =
Min(front_, reinterpret_cast<intptr_t*>(p->body_limit()));
int entries_count = (head_end - head_start) / kEntrySizeInWords;
emergency_stack_ = new List<Entry>(2 * entries_count);
while (head_start != head_end) {
int size = *(head_start++);
HeapObject* obj = reinterpret_cast<HeapObject*>(*(head_start++));
emergency_stack_->Add(Entry(obj, size));
}
rear_ = head_end;
}
void Heap::Scavenge() { void Heap::Scavenge() {
#ifdef DEBUG #ifdef DEBUG
if (FLAG_verify_heap) VerifyNonPointerSpacePointers(); if (FLAG_verify_heap) VerifyNonPointerSpacePointers();
@ -1036,7 +1072,7 @@ void Heap::Scavenge() {
// frees up its size in bytes from the top of the new space, and // frees up its size in bytes from the top of the new space, and
// objects are at least one pointer in size. // objects are at least one pointer in size.
Address new_space_front = new_space_.ToSpaceStart(); Address new_space_front = new_space_.ToSpaceStart();
promotion_queue_.Initialize(new_space_.ToSpaceEnd()); promotion_queue_.Initialize();
#ifdef DEBUG #ifdef DEBUG
store_buffer()->Clean(); store_buffer()->Clean();
@ -1076,10 +1112,11 @@ void Heap::Scavenge() {
&scavenge_visitor); &scavenge_visitor);
new_space_front = DoScavenge(&scavenge_visitor, new_space_front); new_space_front = DoScavenge(&scavenge_visitor, new_space_front);
UpdateNewSpaceReferencesInExternalStringTable( UpdateNewSpaceReferencesInExternalStringTable(
&UpdateNewSpaceReferenceInExternalStringTableEntry); &UpdateNewSpaceReferenceInExternalStringTableEntry);
promotion_queue_.Destroy();
LiveObjectList::UpdateReferencesForScavengeGC(); LiveObjectList::UpdateReferencesForScavengeGC();
isolate()->runtime_profiler()->UpdateSamplesAfterScavenge(); isolate()->runtime_profiler()->UpdateSamplesAfterScavenge();
incremental_marking()->UpdateMarkingDequeAfterScavenge(); incremental_marking()->UpdateMarkingDequeAfterScavenge();
@ -1486,6 +1523,7 @@ class ScavengingVisitor : public StaticVisitorBase {
} }
} }
MaybeObject* allocation = heap->new_space()->AllocateRaw(object_size); MaybeObject* allocation = heap->new_space()->AllocateRaw(object_size);
heap->promotion_queue()->SetNewLimit(heap->new_space()->top());
Object* result = allocation->ToObjectUnchecked(); Object* result = allocation->ToObjectUnchecked();
*slot = MigrateObject(heap, object, HeapObject::cast(result), object_size); *slot = MigrateObject(heap, object, HeapObject::cast(result), object_size);

View File

@ -282,24 +282,58 @@ class HeapDebugUtils;
// by it's size to avoid dereferencing a map pointer for scanning. // by it's size to avoid dereferencing a map pointer for scanning.
class PromotionQueue { class PromotionQueue {
public: public:
PromotionQueue() : front_(NULL), rear_(NULL) { } PromotionQueue(Heap* heap)
: front_(NULL),
rear_(NULL),
limit_(NULL),
emergency_stack_(0),
heap_(heap) { }
void Initialize(Address start_address) { void Initialize();
// Assumes that a NewSpacePage exactly fits a number of promotion queue
// entries (where each is a pair of intptr_t). This allows us to simplify void Destroy() {
// the test fpr when to switch pages. ASSERT(is_empty());
ASSERT((Page::kPageSize - MemoryChunk::kBodyOffset) % (2 * kPointerSize) delete emergency_stack_;
== 0); emergency_stack_ = NULL;
ASSERT(NewSpacePage::IsAtEnd(start_address));
front_ = rear_ = reinterpret_cast<intptr_t*>(start_address);
} }
bool is_empty() { return front_ == rear_; } inline void ActivateGuardIfOnTheSamePage();
Page* GetHeadPage() {
return Page::FromAllocationTop(reinterpret_cast<Address>(rear_));
}
void SetNewLimit(Address limit) {
if (!guard_) {
return;
}
ASSERT(GetHeadPage() == Page::FromAllocationTop(limit));
limit_ = reinterpret_cast<intptr_t*>(limit);
if (limit_ <= rear_) {
return;
}
RelocateQueueHead();
}
bool is_empty() {
return (front_ == rear_) &&
(emergency_stack_ == NULL || emergency_stack_->length() == 0);
}
inline void insert(HeapObject* target, int size); inline void insert(HeapObject* target, int size);
void remove(HeapObject** target, int* size) { void remove(HeapObject** target, int* size) {
ASSERT(!is_empty()); ASSERT(!is_empty());
if (front_ == rear_) {
Entry e = emergency_stack_->RemoveLast();
*target = e.obj_;
*size = e.size_;
return;
}
if (NewSpacePage::IsAtStart(reinterpret_cast<Address>(front_))) { if (NewSpacePage::IsAtStart(reinterpret_cast<Address>(front_))) {
NewSpacePage* front_page = NewSpacePage* front_page =
NewSpacePage::FromAddress(reinterpret_cast<Address>(front_)); NewSpacePage::FromAddress(reinterpret_cast<Address>(front_));
@ -318,6 +352,23 @@ class PromotionQueue {
// The front of the queue is higher in the memory page chain than the rear. // The front of the queue is higher in the memory page chain than the rear.
intptr_t* front_; intptr_t* front_;
intptr_t* rear_; intptr_t* rear_;
intptr_t* limit_;
bool guard_;
static const int kEntrySizeInWords = 2;
struct Entry {
Entry(HeapObject* obj, int size) : obj_(obj), size_(size) { }
HeapObject* obj_;
int size_;
};
List<Entry>* emergency_stack_;
Heap* heap_;
void RelocateQueueHead();
DISALLOW_COPY_AND_ASSIGN(PromotionQueue); DISALLOW_COPY_AND_ASSIGN(PromotionQueue);
}; };

View File

@ -293,30 +293,12 @@ MaybeObject* PagedSpace::AllocateRaw(int size_in_bytes) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// NewSpace // NewSpace
MaybeObject* NewSpace::AllocateRawInternal(int size_in_bytes) {
MaybeObject* NewSpace::AllocateRaw(int size_in_bytes) {
Address old_top = allocation_info_.top; Address old_top = allocation_info_.top;
if (allocation_info_.limit - old_top < size_in_bytes) { if (allocation_info_.limit - old_top < size_in_bytes) {
Address new_top = old_top + size_in_bytes; return SlowAllocateRaw(size_in_bytes);
Address high = to_space_.page_high();
if (allocation_info_.limit < high) {
// Incremental marking has lowered the limit to get a
// chance to do a step.
allocation_info_.limit = Min(
allocation_info_.limit + inline_allocation_limit_step_,
high);
int bytes_allocated = static_cast<int>(new_top - top_on_previous_step_);
heap()->incremental_marking()->Step(bytes_allocated);
top_on_previous_step_ = new_top;
return AllocateRawInternal(size_in_bytes);
} else if (AddFreshPage()) {
// Switched to new page. Try allocating again.
int bytes_allocated = static_cast<int>(old_top - top_on_previous_step_);
heap()->incremental_marking()->Step(bytes_allocated);
top_on_previous_step_ = to_space_.page_low();
return AllocateRawInternal(size_in_bytes);
} else {
return Failure::RetryAfterGC();
}
} }
Object* obj = HeapObject::FromAddress(allocation_info_.top); Object* obj = HeapObject::FromAddress(allocation_info_.top);

View File

@ -1012,16 +1012,49 @@ bool NewSpace::AddFreshPage() {
// Failed to get a new page in to-space. // Failed to get a new page in to-space.
return false; return false;
} }
// Clear remainder of current page. // Clear remainder of current page.
int remaining_in_page = Address limit = NewSpacePage::FromLimit(top)->body_limit();
static_cast<int>(NewSpacePage::FromLimit(top)->body_limit() - top); if (heap()->gc_state() == Heap::SCAVENGE) {
heap()->promotion_queue()->SetNewLimit(limit);
heap()->promotion_queue()->ActivateGuardIfOnTheSamePage();
}
int remaining_in_page = static_cast<int>(limit - top);
heap()->CreateFillerObjectAt(top, remaining_in_page); heap()->CreateFillerObjectAt(top, remaining_in_page);
pages_used_++; pages_used_++;
UpdateAllocationInfo(); UpdateAllocationInfo();
return true; return true;
} }
MaybeObject* NewSpace::SlowAllocateRaw(int size_in_bytes) {
Address old_top = allocation_info_.top;
Address new_top = old_top + size_in_bytes;
Address high = to_space_.page_high();
if (allocation_info_.limit < high) {
// Incremental marking has lowered the limit to get a
// chance to do a step.
allocation_info_.limit = Min(
allocation_info_.limit + inline_allocation_limit_step_,
high);
int bytes_allocated = static_cast<int>(new_top - top_on_previous_step_);
heap()->incremental_marking()->Step(bytes_allocated);
top_on_previous_step_ = new_top;
return AllocateRaw(size_in_bytes);
} else if (AddFreshPage()) {
// Switched to new page. Try allocating again.
int bytes_allocated = static_cast<int>(old_top - top_on_previous_step_);
heap()->incremental_marking()->Step(bytes_allocated);
top_on_previous_step_ = to_space_.page_low();
return AllocateRaw(size_in_bytes);
} else {
return Failure::RetryAfterGC();
}
}
#ifdef DEBUG #ifdef DEBUG
// We do not use the SemiSpaceIterator because verification doesn't assume // We do not use the SemiSpaceIterator because verification doesn't assume
// that it works (it depends on the invariants we are checking). // that it works (it depends on the invariants we are checking).
@ -1904,7 +1937,7 @@ bool NewSpace::ReserveSpace(int bytes) {
// marking. The most reliable way to ensure that there is linear space is // marking. The most reliable way to ensure that there is linear space is
// to do the allocation, then rewind the limit. // to do the allocation, then rewind the limit.
ASSERT(bytes <= InitialCapacity()); ASSERT(bytes <= InitialCapacity());
MaybeObject* maybe = AllocateRawInternal(bytes); MaybeObject* maybe = AllocateRaw(bytes);
Object* object = NULL; Object* object = NULL;
if (!maybe->ToObject(&object)) return false; if (!maybe->ToObject(&object)) return false;
HeapObject* allocation = HeapObject::cast(object); HeapObject* allocation = HeapObject::cast(object);

View File

@ -2140,9 +2140,7 @@ class NewSpace : public Space {
Address* allocation_top_address() { return &allocation_info_.top; } Address* allocation_top_address() { return &allocation_info_.top; }
Address* allocation_limit_address() { return &allocation_info_.limit; } Address* allocation_limit_address() { return &allocation_info_.limit; }
MUST_USE_RESULT MaybeObject* AllocateRaw(int size_in_bytes) { MUST_USE_RESULT INLINE(MaybeObject* AllocateRaw(int size_in_bytes));
return AllocateRawInternal(size_in_bytes);
}
// 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();
@ -2268,8 +2266,7 @@ class NewSpace : public Space {
HistogramInfo* allocated_histogram_; HistogramInfo* allocated_histogram_;
HistogramInfo* promoted_histogram_; HistogramInfo* promoted_histogram_;
// Implementation of AllocateRaw. MUST_USE_RESULT MaybeObject* SlowAllocateRaw(int size_in_bytes);
MUST_USE_RESULT inline MaybeObject* AllocateRawInternal(int size_in_bytes);
friend class SemiSpaceIterator; friend class SemiSpaceIterator;