Index: src/spaces.cc |
=================================================================== |
--- src/spaces.cc (revision 9327) |
+++ src/spaces.cc (working copy) |
@@ -35,52 +35,66 @@ |
namespace v8 { |
namespace internal { |
-// For contiguous spaces, top should be in the space (or at the end) and limit |
-// should be the end of the space. |
-#define ASSERT_SEMISPACE_ALLOCATION_INFO(info, space) \ |
- ASSERT((space).low() <= (info).top \ |
- && (info).top <= (space).high() \ |
- && (info).limit == (space).high()) |
// ---------------------------------------------------------------------------- |
// HeapObjectIterator |
HeapObjectIterator::HeapObjectIterator(PagedSpace* space) { |
- Initialize(space->bottom(), space->top(), NULL); |
+ // You can't actually iterate over the anchor page. It is not a real page, |
+ // just an anchor for the double linked page list. Initialize as if we have |
+ // reached the end of the anchor page, then the first iteration will move on |
+ // to the first page. |
+ Initialize(space, |
+ NULL, |
+ NULL, |
+ kAllPagesInSpace, |
+ NULL); |
} |
HeapObjectIterator::HeapObjectIterator(PagedSpace* space, |
HeapObjectCallback size_func) { |
- Initialize(space->bottom(), space->top(), size_func); |
+ // You can't actually iterate over the anchor page. It is not a real page, |
+ // just an anchor for the double linked page list. Initialize the current |
+ // address and end as NULL, then the first iteration will move on |
+ // to the first page. |
+ Initialize(space, |
+ NULL, |
+ NULL, |
+ kAllPagesInSpace, |
+ size_func); |
} |
-HeapObjectIterator::HeapObjectIterator(PagedSpace* space, Address start) { |
- Initialize(start, space->top(), NULL); |
-} |
- |
- |
-HeapObjectIterator::HeapObjectIterator(PagedSpace* space, Address start, |
- HeapObjectCallback size_func) { |
- Initialize(start, space->top(), size_func); |
-} |
- |
- |
HeapObjectIterator::HeapObjectIterator(Page* page, |
HeapObjectCallback size_func) { |
- Initialize(page->ObjectAreaStart(), page->AllocationTop(), size_func); |
+ Space* owner = page->owner(); |
+ ASSERT(owner == HEAP->old_pointer_space() || |
+ owner == HEAP->old_data_space() || |
+ owner == HEAP->map_space() || |
+ owner == HEAP->cell_space() || |
+ owner == HEAP->code_space()); |
+ Initialize(reinterpret_cast<PagedSpace*>(owner), |
+ page->ObjectAreaStart(), |
+ page->ObjectAreaEnd(), |
+ kOnePageOnly, |
+ size_func); |
+ ASSERT(page->WasSweptPrecisely()); |
} |
-void HeapObjectIterator::Initialize(Address cur, Address end, |
+void HeapObjectIterator::Initialize(PagedSpace* space, |
+ Address cur, Address end, |
+ HeapObjectIterator::PageMode mode, |
HeapObjectCallback size_f) { |
+ // Check that we actually can iterate this space. |
+ ASSERT(!space->was_swept_conservatively()); |
+ |
+ space_ = space; |
cur_addr_ = cur; |
- end_addr_ = end; |
- end_page_ = Page::FromAllocationTop(end); |
+ cur_end_ = end; |
+ page_mode_ = mode; |
size_func_ = size_f; |
- Page* p = Page::FromAllocationTop(cur_addr_); |
- cur_limit_ = (p == end_page_) ? end_addr_ : p->AllocationTop(); |
#ifdef DEBUG |
Verify(); |
@@ -88,63 +102,35 @@ |
} |
-HeapObject* HeapObjectIterator::FromNextPage() { |
- if (cur_addr_ == end_addr_) return NULL; |
- |
- Page* cur_page = Page::FromAllocationTop(cur_addr_); |
+// We have hit the end of the page and should advance to the next block of |
+// objects. This happens at the end of the page. |
+bool HeapObjectIterator::AdvanceToNextPage() { |
+ ASSERT(cur_addr_ == cur_end_); |
+ if (page_mode_ == kOnePageOnly) return false; |
+ Page* cur_page; |
+ if (cur_addr_ == NULL) { |
+ cur_page = space_->anchor(); |
+ } else { |
+ cur_page = Page::FromAddress(cur_addr_ - 1); |
+ ASSERT(cur_addr_ == cur_page->ObjectAreaEnd()); |
+ } |
cur_page = cur_page->next_page(); |
- ASSERT(cur_page->is_valid()); |
- |
+ if (cur_page == space_->anchor()) return false; |
cur_addr_ = cur_page->ObjectAreaStart(); |
- cur_limit_ = (cur_page == end_page_) ? end_addr_ : cur_page->AllocationTop(); |
- |
- if (cur_addr_ == end_addr_) return NULL; |
- ASSERT(cur_addr_ < cur_limit_); |
-#ifdef DEBUG |
- Verify(); |
-#endif |
- return FromCurrentPage(); |
+ cur_end_ = cur_page->ObjectAreaEnd(); |
+ ASSERT(cur_page->WasSweptPrecisely()); |
+ return true; |
} |
#ifdef DEBUG |
void HeapObjectIterator::Verify() { |
- Page* p = Page::FromAllocationTop(cur_addr_); |
- ASSERT(p == Page::FromAllocationTop(cur_limit_)); |
- ASSERT(p->Offset(cur_addr_) <= p->Offset(cur_limit_)); |
+ // TODO(gc): We should do something here. |
} |
#endif |
// ----------------------------------------------------------------------------- |
-// PageIterator |
- |
-PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) { |
- prev_page_ = NULL; |
- switch (mode) { |
- case PAGES_IN_USE: |
- stop_page_ = space->AllocationTopPage(); |
- break; |
- case PAGES_USED_BY_MC: |
- stop_page_ = space->MCRelocationTopPage(); |
- break; |
- case ALL_PAGES: |
-#ifdef DEBUG |
- // Verify that the cached last page in the space is actually the |
- // last page. |
- for (Page* p = space->first_page_; p->is_valid(); p = p->next_page()) { |
- if (!p->next_page()->is_valid()) { |
- ASSERT(space->last_page_ == p); |
- } |
- } |
-#endif |
- stop_page_ = space->last_page_; |
- break; |
- } |
-} |
- |
- |
-// ----------------------------------------------------------------------------- |
// CodeRange |
@@ -171,7 +157,12 @@ |
// We are sure that we have mapped a block of requested addresses. |
ASSERT(code_range_->size() == requested); |
LOG(isolate_, NewEvent("CodeRange", code_range_->address(), requested)); |
- allocation_list_.Add(FreeBlock(code_range_->address(), code_range_->size())); |
+ Address base = reinterpret_cast<Address>(code_range_->address()); |
+ Address aligned_base = |
+ RoundUp(reinterpret_cast<Address>(code_range_->address()), |
+ MemoryChunk::kAlignment); |
+ size_t size = code_range_->size() - (aligned_base - base); |
+ allocation_list_.Add(FreeBlock(aligned_base, size)); |
current_allocation_block_index_ = 0; |
return true; |
} |
@@ -228,7 +219,8 @@ |
-void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { |
+Address CodeRange::AllocateRawMemory(const size_t requested, |
+ size_t* allocated) { |
ASSERT(current_allocation_block_index_ < allocation_list_.length()); |
if (requested > allocation_list_[current_allocation_block_index_].size) { |
// Find an allocation block large enough. This function call may |
@@ -236,13 +228,16 @@ |
GetNextAllocationBlock(requested); |
} |
// Commit the requested memory at the start of the current allocation block. |
- *allocated = RoundUp(requested, Page::kPageSize); |
+ size_t aligned_requested = RoundUp(requested, MemoryChunk::kAlignment); |
FreeBlock current = allocation_list_[current_allocation_block_index_]; |
- if (*allocated >= current.size - Page::kPageSize) { |
+ if (aligned_requested >= (current.size - Page::kPageSize)) { |
// Don't leave a small free block, useless for a large object or chunk. |
*allocated = current.size; |
+ } else { |
+ *allocated = aligned_requested; |
} |
ASSERT(*allocated <= current.size); |
+ ASSERT(IsAddressAligned(current.start, MemoryChunk::kAlignment)); |
if (!code_range_->Commit(current.start, *allocated, true)) { |
*allocated = 0; |
return NULL; |
@@ -256,7 +251,8 @@ |
} |
-void CodeRange::FreeRawMemory(void* address, size_t length) { |
+void CodeRange::FreeRawMemory(Address address, size_t length) { |
+ ASSERT(IsAddressAligned(address, MemoryChunk::kAlignment)); |
free_list_.Add(FreeBlock(address, length)); |
code_range_->Uncommit(address, length); |
} |
@@ -274,306 +270,313 @@ |
// MemoryAllocator |
// |
-// 270 is an estimate based on the static default heap size of a pair of 256K |
-// semispaces and a 64M old generation. |
-const int kEstimatedNumberOfChunks = 270; |
- |
- |
MemoryAllocator::MemoryAllocator(Isolate* isolate) |
: isolate_(isolate), |
capacity_(0), |
capacity_executable_(0), |
size_(0), |
- size_executable_(0), |
- initial_chunk_(NULL), |
- chunks_(kEstimatedNumberOfChunks), |
- free_chunk_ids_(kEstimatedNumberOfChunks), |
- max_nof_chunks_(0), |
- top_(0) { |
+ size_executable_(0) { |
} |
-void MemoryAllocator::Push(int free_chunk_id) { |
- ASSERT(max_nof_chunks_ > 0); |
- ASSERT(top_ < max_nof_chunks_); |
- free_chunk_ids_[top_++] = free_chunk_id; |
-} |
- |
- |
-int MemoryAllocator::Pop() { |
- ASSERT(top_ > 0); |
- return free_chunk_ids_[--top_]; |
-} |
- |
- |
bool MemoryAllocator::Setup(intptr_t capacity, intptr_t capacity_executable) { |
capacity_ = RoundUp(capacity, Page::kPageSize); |
capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize); |
ASSERT_GE(capacity_, capacity_executable_); |
- // Over-estimate the size of chunks_ array. It assumes the expansion of old |
- // space is always in the unit of a chunk (kChunkSize) except the last |
- // expansion. |
- // |
- // Due to alignment, allocated space might be one page less than required |
- // number (kPagesPerChunk) of pages for old spaces. |
- // |
- // Reserve two chunk ids for semispaces, one for map space, one for old |
- // space, and one for code space. |
- max_nof_chunks_ = |
- static_cast<int>((capacity_ / (kChunkSize - Page::kPageSize))) + 5; |
- if (max_nof_chunks_ > kMaxNofChunks) return false; |
- |
size_ = 0; |
size_executable_ = 0; |
- ChunkInfo info; // uninitialized element. |
- for (int i = max_nof_chunks_ - 1; i >= 0; i--) { |
- chunks_.Add(info); |
- free_chunk_ids_.Add(i); |
- } |
- top_ = max_nof_chunks_; |
+ |
return true; |
} |
void MemoryAllocator::TearDown() { |
- for (int i = 0; i < max_nof_chunks_; i++) { |
- if (chunks_[i].address() != NULL) DeleteChunk(i); |
- } |
- chunks_.Clear(); |
- free_chunk_ids_.Clear(); |
- |
- if (initial_chunk_ != NULL) { |
- LOG(isolate_, DeleteEvent("InitialChunk", initial_chunk_->address())); |
- delete initial_chunk_; |
- initial_chunk_ = NULL; |
- } |
- |
- ASSERT(top_ == max_nof_chunks_); // all chunks are free |
- top_ = 0; |
+ // Check that spaces were torn down before MemoryAllocator. |
+ ASSERT(size_ == 0); |
+ // TODO(gc) this will be true again when we fix FreeMemory. |
+ // ASSERT(size_executable_ == 0); |
capacity_ = 0; |
capacity_executable_ = 0; |
- size_ = 0; |
- max_nof_chunks_ = 0; |
} |
-void* MemoryAllocator::AllocateRawMemory(const size_t requested, |
- size_t* allocated, |
- Executability executable) { |
- if (size_ + static_cast<size_t>(requested) > static_cast<size_t>(capacity_)) { |
- return NULL; |
- } |
+void MemoryAllocator::FreeMemory(VirtualMemory* reservation, |
+ Executability executable) { |
+ // TODO(gc) make code_range part of memory allocator? |
+ ASSERT(reservation->IsReserved()); |
+ size_t size = reservation->size(); |
+ ASSERT(size_ >= size); |
+ size_ -= size; |
- void* mem; |
+ isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size)); |
+ |
if (executable == EXECUTABLE) { |
- // Check executable memory limit. |
- if (size_executable_ + requested > |
- static_cast<size_t>(capacity_executable_)) { |
- LOG(isolate_, |
- StringEvent("MemoryAllocator::AllocateRawMemory", |
- "V8 Executable Allocation capacity exceeded")); |
- return NULL; |
- } |
- // Allocate executable memory either from code range or from the |
- // OS. |
- if (isolate_->code_range()->exists()) { |
- mem = isolate_->code_range()->AllocateRawMemory(requested, allocated); |
- } else { |
- mem = OS::Allocate(requested, allocated, true); |
- } |
- // Update executable memory size. |
- size_executable_ += static_cast<int>(*allocated); |
- } else { |
- mem = OS::Allocate(requested, allocated, false); |
+ ASSERT(size_executable_ >= size); |
+ size_executable_ -= size; |
} |
- int alloced = static_cast<int>(*allocated); |
- size_ += alloced; |
- |
-#ifdef DEBUG |
- ZapBlock(reinterpret_cast<Address>(mem), alloced); |
-#endif |
- isolate_->counters()->memory_allocated()->Increment(alloced); |
- return mem; |
+ // Code which is part of the code-range does not have its own VirtualMemory. |
+ ASSERT(!isolate_->code_range()->contains( |
+ static_cast<Address>(reservation->address()))); |
+ ASSERT(executable == NOT_EXECUTABLE || !isolate_->code_range()->exists()); |
+ reservation->Release(); |
} |
-void MemoryAllocator::FreeRawMemory(void* mem, |
- size_t length, |
- Executability executable) { |
-#ifdef DEBUG |
- // Do not try to zap the guard page. |
- size_t guard_size = (executable == EXECUTABLE) ? Page::kPageSize : 0; |
- ZapBlock(reinterpret_cast<Address>(mem) + guard_size, length - guard_size); |
-#endif |
- if (isolate_->code_range()->contains(static_cast<Address>(mem))) { |
- isolate_->code_range()->FreeRawMemory(mem, length); |
+void MemoryAllocator::FreeMemory(Address base, |
+ size_t size, |
+ Executability executable) { |
+ // TODO(gc) make code_range part of memory allocator? |
+ ASSERT(size_ >= size); |
+ size_ -= size; |
+ |
+ isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size)); |
+ |
+ if (executable == EXECUTABLE) { |
+ ASSERT(size_executable_ >= size); |
+ size_executable_ -= size; |
+ } |
+ if (isolate_->code_range()->contains(static_cast<Address>(base))) { |
+ ASSERT(executable == EXECUTABLE); |
+ isolate_->code_range()->FreeRawMemory(base, size); |
} else { |
- OS::Free(mem, length); |
+ ASSERT(executable == NOT_EXECUTABLE || !isolate_->code_range()->exists()); |
+ VirtualMemory::ReleaseRegion(base, size); |
} |
- isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(length)); |
- size_ -= static_cast<int>(length); |
- if (executable == EXECUTABLE) size_executable_ -= static_cast<int>(length); |
+} |
- ASSERT(size_ >= 0); |
- ASSERT(size_executable_ >= 0); |
+ |
+Address MemoryAllocator::ReserveAlignedMemory(size_t size, |
+ size_t alignment, |
+ VirtualMemory* controller) { |
+ VirtualMemory reservation(size, alignment); |
+ |
+ if (!reservation.IsReserved()) return NULL; |
+ size_ += reservation.size(); |
+ Address base = RoundUp(static_cast<Address>(reservation.address()), |
+ alignment); |
+ controller->TakeControl(&reservation); |
+ return base; |
} |
-void MemoryAllocator::PerformAllocationCallback(ObjectSpace space, |
- AllocationAction action, |
- size_t size) { |
- for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { |
- MemoryAllocationCallbackRegistration registration = |
- memory_allocation_callbacks_[i]; |
- if ((registration.space & space) == space && |
- (registration.action & action) == action) |
- registration.callback(space, action, static_cast<int>(size)); |
+Address MemoryAllocator::AllocateAlignedMemory(size_t size, |
+ size_t alignment, |
+ Executability executable, |
+ VirtualMemory* controller) { |
+ VirtualMemory reservation; |
+ Address base = ReserveAlignedMemory(size, alignment, &reservation); |
+ if (base == NULL) return NULL; |
+ if (!reservation.Commit(base, |
+ size, |
+ executable == EXECUTABLE)) { |
+ return NULL; |
} |
+ controller->TakeControl(&reservation); |
+ return base; |
} |
-bool MemoryAllocator::MemoryAllocationCallbackRegistered( |
- MemoryAllocationCallback callback) { |
- for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { |
- if (memory_allocation_callbacks_[i].callback == callback) return true; |
- } |
- return false; |
+void Page::InitializeAsAnchor(PagedSpace* owner) { |
+ set_owner(owner); |
+ set_prev_page(this); |
+ set_next_page(this); |
} |
-void MemoryAllocator::AddMemoryAllocationCallback( |
- MemoryAllocationCallback callback, |
- ObjectSpace space, |
- AllocationAction action) { |
- ASSERT(callback != NULL); |
- MemoryAllocationCallbackRegistration registration(callback, space, action); |
- ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback)); |
- return memory_allocation_callbacks_.Add(registration); |
+NewSpacePage* NewSpacePage::Initialize(Heap* heap, |
+ Address start, |
+ SemiSpace* semi_space) { |
+ MemoryChunk* chunk = MemoryChunk::Initialize(heap, |
+ start, |
+ Page::kPageSize, |
+ NOT_EXECUTABLE, |
+ semi_space); |
+ chunk->set_next_chunk(NULL); |
+ chunk->set_prev_chunk(NULL); |
+ chunk->initialize_scan_on_scavenge(true); |
+ bool in_to_space = (semi_space->id() != kFromSpace); |
+ chunk->SetFlag(in_to_space ? MemoryChunk::IN_TO_SPACE |
+ : MemoryChunk::IN_FROM_SPACE); |
+ ASSERT(!chunk->IsFlagSet(in_to_space ? MemoryChunk::IN_FROM_SPACE |
+ : MemoryChunk::IN_TO_SPACE)); |
+ NewSpacePage* page = static_cast<NewSpacePage*>(chunk); |
+ heap->incremental_marking()->SetNewSpacePageFlags(page); |
+ return page; |
} |
-void MemoryAllocator::RemoveMemoryAllocationCallback( |
- MemoryAllocationCallback callback) { |
- ASSERT(callback != NULL); |
- for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { |
- if (memory_allocation_callbacks_[i].callback == callback) { |
- memory_allocation_callbacks_.Remove(i); |
- return; |
- } |
- } |
- UNREACHABLE(); |
+void NewSpacePage::InitializeAsAnchor(SemiSpace* semi_space) { |
+ set_owner(semi_space); |
+ set_next_chunk(this); |
+ set_prev_chunk(this); |
+ // Flags marks this invalid page as not being in new-space. |
+ // All real new-space pages will be in new-space. |
+ SetFlags(0, ~0); |
} |
-void* MemoryAllocator::ReserveInitialChunk(const size_t requested) { |
- ASSERT(initial_chunk_ == NULL); |
- initial_chunk_ = new VirtualMemory(requested); |
- CHECK(initial_chunk_ != NULL); |
- if (!initial_chunk_->IsReserved()) { |
- delete initial_chunk_; |
- initial_chunk_ = NULL; |
- return NULL; |
- } |
+MemoryChunk* MemoryChunk::Initialize(Heap* heap, |
+ Address base, |
+ size_t size, |
+ Executability executable, |
+ Space* owner) { |
+ MemoryChunk* chunk = FromAddress(base); |
- // We are sure that we have mapped a block of requested addresses. |
- ASSERT(initial_chunk_->size() == requested); |
- LOG(isolate_, |
- NewEvent("InitialChunk", initial_chunk_->address(), requested)); |
- size_ += static_cast<int>(requested); |
- return initial_chunk_->address(); |
-} |
+ ASSERT(base == chunk->address()); |
+ chunk->heap_ = heap; |
+ chunk->size_ = size; |
+ chunk->flags_ = 0; |
+ chunk->set_owner(owner); |
+ chunk->InitializeReservedMemory(); |
+ chunk->slots_buffer_ = NULL; |
+ chunk->skip_list_ = NULL; |
+ Bitmap::Clear(chunk); |
+ chunk->initialize_scan_on_scavenge(false); |
+ chunk->SetFlag(WAS_SWEPT_PRECISELY); |
-static int PagesInChunk(Address start, size_t size) { |
- // The first page starts on the first page-aligned address from start onward |
- // and the last page ends on the last page-aligned address before |
- // start+size. Page::kPageSize is a power of two so we can divide by |
- // shifting. |
- return static_cast<int>((RoundDown(start + size, Page::kPageSize) |
- - RoundUp(start, Page::kPageSize)) >> kPageSizeBits); |
+ ASSERT(OFFSET_OF(MemoryChunk, flags_) == kFlagsOffset); |
+ |
+ if (executable == EXECUTABLE) chunk->SetFlag(IS_EXECUTABLE); |
+ |
+ if (owner == heap->old_data_space()) chunk->SetFlag(CONTAINS_ONLY_DATA); |
+ |
+ return chunk; |
} |
-Page* MemoryAllocator::AllocatePages(int requested_pages, |
- int* allocated_pages, |
- PagedSpace* owner) { |
- if (requested_pages <= 0) return Page::FromAddress(NULL); |
- size_t chunk_size = requested_pages * Page::kPageSize; |
+void MemoryChunk::InsertAfter(MemoryChunk* other) { |
+ next_chunk_ = other->next_chunk_; |
+ prev_chunk_ = other; |
+ other->next_chunk_->prev_chunk_ = this; |
+ other->next_chunk_ = this; |
+} |
- void* chunk = AllocateRawMemory(chunk_size, &chunk_size, owner->executable()); |
- if (chunk == NULL) return Page::FromAddress(NULL); |
- LOG(isolate_, NewEvent("PagedChunk", chunk, chunk_size)); |
- *allocated_pages = PagesInChunk(static_cast<Address>(chunk), chunk_size); |
+void MemoryChunk::Unlink() { |
+ if (!InNewSpace() && IsFlagSet(SCAN_ON_SCAVENGE)) { |
+ heap_->decrement_scan_on_scavenge_pages(); |
+ ClearFlag(SCAN_ON_SCAVENGE); |
+ } |
+ next_chunk_->prev_chunk_ = prev_chunk_; |
+ prev_chunk_->next_chunk_ = next_chunk_; |
+ prev_chunk_ = NULL; |
+ next_chunk_ = NULL; |
+} |
- // We may 'lose' a page due to alignment. |
- ASSERT(*allocated_pages >= kPagesPerChunk - 1); |
- size_t guard_size = (owner->executable() == EXECUTABLE) ? Page::kPageSize : 0; |
+MemoryChunk* MemoryAllocator::AllocateChunk(intptr_t body_size, |
+ Executability executable, |
+ Space* owner) { |
+ size_t chunk_size = MemoryChunk::kObjectStartOffset + body_size; |
+ Heap* heap = isolate_->heap(); |
+ Address base = NULL; |
+ VirtualMemory reservation; |
+ if (executable == EXECUTABLE) { |
+ // Check executable memory limit. |
+ if (size_executable_ + chunk_size > capacity_executable_) { |
+ LOG(isolate_, |
+ StringEvent("MemoryAllocator::AllocateRawMemory", |
+ "V8 Executable Allocation capacity exceeded")); |
+ return NULL; |
+ } |
- // Check that we got at least one page that we can use. |
- if (*allocated_pages <= ((guard_size != 0) ? 1 : 0)) { |
- FreeRawMemory(chunk, |
- chunk_size, |
- owner->executable()); |
- LOG(isolate_, DeleteEvent("PagedChunk", chunk)); |
- return Page::FromAddress(NULL); |
+ // Allocate executable memory either from code range or from the |
+ // OS. |
+ if (isolate_->code_range()->exists()) { |
+ base = isolate_->code_range()->AllocateRawMemory(chunk_size, &chunk_size); |
+ ASSERT(IsAligned(reinterpret_cast<intptr_t>(base), |
+ MemoryChunk::kAlignment)); |
+ if (base == NULL) return NULL; |
+ size_ += chunk_size; |
+ // Update executable memory size. |
+ size_executable_ += chunk_size; |
+ } else { |
+ base = AllocateAlignedMemory(chunk_size, |
+ MemoryChunk::kAlignment, |
+ executable, |
+ &reservation); |
+ if (base == NULL) return NULL; |
+ // Update executable memory size. |
+ size_executable_ += reservation.size(); |
+ } |
+ } else { |
+ base = AllocateAlignedMemory(chunk_size, |
+ MemoryChunk::kAlignment, |
+ executable, |
+ &reservation); |
+ |
+ if (base == NULL) return NULL; |
} |
- if (guard_size != 0) { |
- OS::Guard(chunk, guard_size); |
- chunk_size -= guard_size; |
- chunk = static_cast<Address>(chunk) + guard_size; |
- --*allocated_pages; |
+#ifdef DEBUG |
+ ZapBlock(base, chunk_size); |
+#endif |
+ isolate_->counters()->memory_allocated()-> |
+ Increment(static_cast<int>(chunk_size)); |
+ |
+ LOG(isolate_, NewEvent("MemoryChunk", base, chunk_size)); |
+ if (owner != NULL) { |
+ ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity()); |
+ PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size); |
} |
- int chunk_id = Pop(); |
- chunks_[chunk_id].init(static_cast<Address>(chunk), chunk_size, owner); |
+ MemoryChunk* result = MemoryChunk::Initialize(heap, |
+ base, |
+ chunk_size, |
+ executable, |
+ owner); |
+ result->set_reserved_memory(&reservation); |
+ return result; |
+} |
- ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity()); |
- PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size); |
- Page* new_pages = InitializePagesInChunk(chunk_id, *allocated_pages, owner); |
- return new_pages; |
+Page* MemoryAllocator::AllocatePage(PagedSpace* owner, |
+ Executability executable) { |
+ MemoryChunk* chunk = AllocateChunk(Page::kObjectAreaSize, executable, owner); |
+ |
+ if (chunk == NULL) return NULL; |
+ |
+ return Page::Initialize(isolate_->heap(), chunk, executable, owner); |
} |
-Page* MemoryAllocator::CommitPages(Address start, size_t size, |
- PagedSpace* owner, int* num_pages) { |
- ASSERT(start != NULL); |
- *num_pages = PagesInChunk(start, size); |
- ASSERT(*num_pages > 0); |
- ASSERT(initial_chunk_ != NULL); |
- ASSERT(InInitialChunk(start)); |
- ASSERT(InInitialChunk(start + size - 1)); |
- if (!initial_chunk_->Commit(start, size, owner->executable() == EXECUTABLE)) { |
- return Page::FromAddress(NULL); |
+LargePage* MemoryAllocator::AllocateLargePage(intptr_t object_size, |
+ Executability executable, |
+ Space* owner) { |
+ MemoryChunk* chunk = AllocateChunk(object_size, executable, owner); |
+ if (chunk == NULL) return NULL; |
+ return LargePage::Initialize(isolate_->heap(), chunk); |
+} |
+ |
+ |
+void MemoryAllocator::Free(MemoryChunk* chunk) { |
+ LOG(isolate_, DeleteEvent("MemoryChunk", chunk)); |
+ if (chunk->owner() != NULL) { |
+ ObjectSpace space = |
+ static_cast<ObjectSpace>(1 << chunk->owner()->identity()); |
+ PerformAllocationCallback(space, kAllocationActionFree, chunk->size()); |
} |
-#ifdef DEBUG |
- ZapBlock(start, size); |
-#endif |
- isolate_->counters()->memory_allocated()->Increment(static_cast<int>(size)); |
- // So long as we correctly overestimated the number of chunks we should not |
- // run out of chunk ids. |
- CHECK(!OutOfChunkIds()); |
- int chunk_id = Pop(); |
- chunks_[chunk_id].init(start, size, owner); |
- return InitializePagesInChunk(chunk_id, *num_pages, owner); |
+ delete chunk->slots_buffer(); |
+ delete chunk->skip_list(); |
+ |
+ VirtualMemory* reservation = chunk->reserved_memory(); |
+ if (reservation->IsReserved()) { |
+ FreeMemory(reservation, chunk->executable()); |
+ } else { |
+ FreeMemory(chunk->address(), |
+ chunk->size(), |
+ chunk->executable()); |
+ } |
} |
bool MemoryAllocator::CommitBlock(Address start, |
size_t size, |
Executability executable) { |
- ASSERT(start != NULL); |
- ASSERT(size > 0); |
- ASSERT(initial_chunk_ != NULL); |
- ASSERT(InInitialChunk(start)); |
- ASSERT(InInitialChunk(start + size - 1)); |
- |
- if (!initial_chunk_->Commit(start, size, executable)) return false; |
+ if (!VirtualMemory::CommitRegion(start, size, executable)) return false; |
#ifdef DEBUG |
ZapBlock(start, size); |
#endif |
@@ -583,13 +586,7 @@ |
bool MemoryAllocator::UncommitBlock(Address start, size_t size) { |
- ASSERT(start != NULL); |
- ASSERT(size > 0); |
- ASSERT(initial_chunk_ != NULL); |
- ASSERT(InInitialChunk(start)); |
- ASSERT(InInitialChunk(start + size - 1)); |
- |
- if (!initial_chunk_->Uncommit(start, size)) return false; |
+ if (!VirtualMemory::UncommitRegion(start, size)) return false; |
isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size)); |
return true; |
} |
@@ -602,133 +599,52 @@ |
} |
-Page* MemoryAllocator::InitializePagesInChunk(int chunk_id, int pages_in_chunk, |
- PagedSpace* owner) { |
- ASSERT(IsValidChunk(chunk_id)); |
- ASSERT(pages_in_chunk > 0); |
- |
- Address chunk_start = chunks_[chunk_id].address(); |
- |
- Address low = RoundUp(chunk_start, Page::kPageSize); |
- |
-#ifdef DEBUG |
- size_t chunk_size = chunks_[chunk_id].size(); |
- Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize); |
- ASSERT(pages_in_chunk <= |
- ((OffsetFrom(high) - OffsetFrom(low)) / Page::kPageSize)); |
-#endif |
- |
- Address page_addr = low; |
- for (int i = 0; i < pages_in_chunk; i++) { |
- Page* p = Page::FromAddress(page_addr); |
- p->heap_ = owner->heap(); |
- p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id; |
- p->InvalidateWatermark(true); |
- p->SetIsLargeObjectPage(false); |
- p->SetAllocationWatermark(p->ObjectAreaStart()); |
- p->SetCachedAllocationWatermark(p->ObjectAreaStart()); |
- page_addr += Page::kPageSize; |
+void MemoryAllocator::PerformAllocationCallback(ObjectSpace space, |
+ AllocationAction action, |
+ size_t size) { |
+ for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { |
+ MemoryAllocationCallbackRegistration registration = |
+ memory_allocation_callbacks_[i]; |
+ if ((registration.space & space) == space && |
+ (registration.action & action) == action) |
+ registration.callback(space, action, static_cast<int>(size)); |
} |
- |
- // Set the next page of the last page to 0. |
- Page* last_page = Page::FromAddress(page_addr - Page::kPageSize); |
- last_page->opaque_header = OffsetFrom(0) | chunk_id; |
- |
- return Page::FromAddress(low); |
} |
-Page* MemoryAllocator::FreePages(Page* p) { |
- if (!p->is_valid()) return p; |
- |
- // Find the first page in the same chunk as 'p' |
- Page* first_page = FindFirstPageInSameChunk(p); |
- Page* page_to_return = Page::FromAddress(NULL); |
- |
- if (p != first_page) { |
- // Find the last page in the same chunk as 'prev'. |
- Page* last_page = FindLastPageInSameChunk(p); |
- first_page = GetNextPage(last_page); // first page in next chunk |
- |
- // set the next_page of last_page to NULL |
- SetNextPage(last_page, Page::FromAddress(NULL)); |
- page_to_return = p; // return 'p' when exiting |
+bool MemoryAllocator::MemoryAllocationCallbackRegistered( |
+ MemoryAllocationCallback callback) { |
+ for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { |
+ if (memory_allocation_callbacks_[i].callback == callback) return true; |
} |
+ return false; |
+} |
- while (first_page->is_valid()) { |
- int chunk_id = GetChunkId(first_page); |
- ASSERT(IsValidChunk(chunk_id)); |
- // Find the first page of the next chunk before deleting this chunk. |
- first_page = GetNextPage(FindLastPageInSameChunk(first_page)); |
- |
- // Free the current chunk. |
- DeleteChunk(chunk_id); |
- } |
- |
- return page_to_return; |
+void MemoryAllocator::AddMemoryAllocationCallback( |
+ MemoryAllocationCallback callback, |
+ ObjectSpace space, |
+ AllocationAction action) { |
+ ASSERT(callback != NULL); |
+ MemoryAllocationCallbackRegistration registration(callback, space, action); |
+ ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback)); |
+ return memory_allocation_callbacks_.Add(registration); |
} |
-void MemoryAllocator::FreeAllPages(PagedSpace* space) { |
- for (int i = 0, length = chunks_.length(); i < length; i++) { |
- if (chunks_[i].owner() == space) { |
- DeleteChunk(i); |
+void MemoryAllocator::RemoveMemoryAllocationCallback( |
+ MemoryAllocationCallback callback) { |
+ ASSERT(callback != NULL); |
+ for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { |
+ if (memory_allocation_callbacks_[i].callback == callback) { |
+ memory_allocation_callbacks_.Remove(i); |
+ return; |
} |
} |
+ UNREACHABLE(); |
} |
-void MemoryAllocator::DeleteChunk(int chunk_id) { |
- ASSERT(IsValidChunk(chunk_id)); |
- |
- ChunkInfo& c = chunks_[chunk_id]; |
- |
- // We cannot free a chunk contained in the initial chunk because it was not |
- // allocated with AllocateRawMemory. Instead we uncommit the virtual |
- // memory. |
- if (InInitialChunk(c.address())) { |
- // TODO(1240712): VirtualMemory::Uncommit has a return value which |
- // is ignored here. |
- initial_chunk_->Uncommit(c.address(), c.size()); |
- Counters* counters = isolate_->counters(); |
- counters->memory_allocated()->Decrement(static_cast<int>(c.size())); |
- } else { |
- LOG(isolate_, DeleteEvent("PagedChunk", c.address())); |
- ObjectSpace space = static_cast<ObjectSpace>(1 << c.owner_identity()); |
- size_t size = c.size(); |
- size_t guard_size = (c.executable() == EXECUTABLE) ? Page::kPageSize : 0; |
- FreeRawMemory(c.address() - guard_size, size + guard_size, c.executable()); |
- PerformAllocationCallback(space, kAllocationActionFree, size); |
- } |
- c.init(NULL, 0, NULL); |
- Push(chunk_id); |
-} |
- |
- |
-Page* MemoryAllocator::FindFirstPageInSameChunk(Page* p) { |
- int chunk_id = GetChunkId(p); |
- ASSERT(IsValidChunk(chunk_id)); |
- |
- Address low = RoundUp(chunks_[chunk_id].address(), Page::kPageSize); |
- return Page::FromAddress(low); |
-} |
- |
- |
-Page* MemoryAllocator::FindLastPageInSameChunk(Page* p) { |
- int chunk_id = GetChunkId(p); |
- ASSERT(IsValidChunk(chunk_id)); |
- |
- Address chunk_start = chunks_[chunk_id].address(); |
- size_t chunk_size = chunks_[chunk_id].size(); |
- |
- Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize); |
- ASSERT(chunk_start <= p->address() && p->address() < high); |
- |
- return Page::FromAddress(high - Page::kPageSize); |
-} |
- |
- |
#ifdef DEBUG |
void MemoryAllocator::ReportStatistics() { |
float pct = static_cast<float>(capacity_ - size_) / capacity_; |
@@ -739,75 +655,6 @@ |
} |
#endif |
- |
-void MemoryAllocator::RelinkPageListInChunkOrder(PagedSpace* space, |
- Page** first_page, |
- Page** last_page, |
- Page** last_page_in_use) { |
- Page* first = NULL; |
- Page* last = NULL; |
- |
- for (int i = 0, length = chunks_.length(); i < length; i++) { |
- ChunkInfo& chunk = chunks_[i]; |
- |
- if (chunk.owner() == space) { |
- if (first == NULL) { |
- Address low = RoundUp(chunk.address(), Page::kPageSize); |
- first = Page::FromAddress(low); |
- } |
- last = RelinkPagesInChunk(i, |
- chunk.address(), |
- chunk.size(), |
- last, |
- last_page_in_use); |
- } |
- } |
- |
- if (first_page != NULL) { |
- *first_page = first; |
- } |
- |
- if (last_page != NULL) { |
- *last_page = last; |
- } |
-} |
- |
- |
-Page* MemoryAllocator::RelinkPagesInChunk(int chunk_id, |
- Address chunk_start, |
- size_t chunk_size, |
- Page* prev, |
- Page** last_page_in_use) { |
- Address page_addr = RoundUp(chunk_start, Page::kPageSize); |
- int pages_in_chunk = PagesInChunk(chunk_start, chunk_size); |
- |
- if (prev->is_valid()) { |
- SetNextPage(prev, Page::FromAddress(page_addr)); |
- } |
- |
- for (int i = 0; i < pages_in_chunk; i++) { |
- Page* p = Page::FromAddress(page_addr); |
- p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id; |
- page_addr += Page::kPageSize; |
- |
- p->InvalidateWatermark(true); |
- if (p->WasInUseBeforeMC()) { |
- *last_page_in_use = p; |
- } |
- } |
- |
- // Set the next page of the last page to 0. |
- Page* last_page = Page::FromAddress(page_addr - Page::kPageSize); |
- last_page->opaque_header = OffsetFrom(0) | chunk_id; |
- |
- if (last_page->WasInUseBeforeMC()) { |
- *last_page_in_use = last_page; |
- } |
- |
- return last_page; |
-} |
- |
- |
// ----------------------------------------------------------------------------- |
// PagedSpace implementation |
@@ -815,7 +662,11 @@ |
intptr_t max_capacity, |
AllocationSpace id, |
Executability executable) |
- : Space(heap, id, executable) { |
+ : Space(heap, id, executable), |
+ free_list_(this), |
+ was_swept_conservatively_(false), |
+ first_unswept_page_(Page::FromAddress(NULL)), |
+ last_unswept_page_(Page::FromAddress(NULL)) { |
max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize) |
* Page::kObjectAreaSize; |
accounting_stats_.Clear(); |
@@ -823,224 +674,84 @@ |
allocation_info_.top = NULL; |
allocation_info_.limit = NULL; |
- mc_forwarding_info_.top = NULL; |
- mc_forwarding_info_.limit = NULL; |
+ anchor_.InitializeAsAnchor(this); |
} |
-bool PagedSpace::Setup(Address start, size_t size) { |
- if (HasBeenSetup()) return false; |
- |
- int num_pages = 0; |
- // Try to use the virtual memory range passed to us. If it is too small to |
- // contain at least one page, ignore it and allocate instead. |
- int pages_in_chunk = PagesInChunk(start, size); |
- if (pages_in_chunk > 0) { |
- first_page_ = Isolate::Current()->memory_allocator()->CommitPages( |
- RoundUp(start, Page::kPageSize), |
- Page::kPageSize * pages_in_chunk, |
- this, &num_pages); |
- } else { |
- int requested_pages = |
- Min(MemoryAllocator::kPagesPerChunk, |
- static_cast<int>(max_capacity_ / Page::kObjectAreaSize)); |
- first_page_ = |
- Isolate::Current()->memory_allocator()->AllocatePages( |
- requested_pages, &num_pages, this); |
- if (!first_page_->is_valid()) return false; |
- } |
- |
- // We are sure that the first page is valid and that we have at least one |
- // page. |
- ASSERT(first_page_->is_valid()); |
- ASSERT(num_pages > 0); |
- accounting_stats_.ExpandSpace(num_pages * Page::kObjectAreaSize); |
- ASSERT(Capacity() <= max_capacity_); |
- |
- // Sequentially clear region marks in the newly allocated |
- // pages and cache the current last page in the space. |
- for (Page* p = first_page_; p->is_valid(); p = p->next_page()) { |
- p->SetRegionMarks(Page::kAllRegionsCleanMarks); |
- last_page_ = p; |
- } |
- |
- // Use first_page_ for allocation. |
- SetAllocationInfo(&allocation_info_, first_page_); |
- |
- page_list_is_chunk_ordered_ = true; |
- |
+bool PagedSpace::Setup() { |
return true; |
} |
bool PagedSpace::HasBeenSetup() { |
- return (Capacity() > 0); |
+ return true; |
} |
void PagedSpace::TearDown() { |
- Isolate::Current()->memory_allocator()->FreeAllPages(this); |
- first_page_ = NULL; |
+ PageIterator iterator(this); |
+ while (iterator.has_next()) { |
+ heap()->isolate()->memory_allocator()->Free(iterator.next()); |
+ } |
+ anchor_.set_next_page(&anchor_); |
+ anchor_.set_prev_page(&anchor_); |
accounting_stats_.Clear(); |
} |
-void PagedSpace::MarkAllPagesClean() { |
- PageIterator it(this, PageIterator::ALL_PAGES); |
- while (it.has_next()) { |
- it.next()->SetRegionMarks(Page::kAllRegionsCleanMarks); |
- } |
-} |
- |
- |
MaybeObject* PagedSpace::FindObject(Address addr) { |
- // Note: this function can only be called before or after mark-compact GC |
- // because it accesses map pointers. |
+ // Note: this function can only be called on precisely swept spaces. |
ASSERT(!heap()->mark_compact_collector()->in_use()); |
if (!Contains(addr)) return Failure::Exception(); |
Page* p = Page::FromAddress(addr); |
- ASSERT(IsUsed(p)); |
- Address cur = p->ObjectAreaStart(); |
- Address end = p->AllocationTop(); |
- while (cur < end) { |
- HeapObject* obj = HeapObject::FromAddress(cur); |
+ HeapObjectIterator it(p, NULL); |
+ for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { |
+ Address cur = obj->address(); |
Address next = cur + obj->Size(); |
if ((cur <= addr) && (addr < next)) return obj; |
- cur = next; |
} |
UNREACHABLE(); |
return Failure::Exception(); |
} |
- |
-bool PagedSpace::IsUsed(Page* page) { |
- PageIterator it(this, PageIterator::PAGES_IN_USE); |
- while (it.has_next()) { |
- if (page == it.next()) return true; |
- } |
- return false; |
-} |
- |
- |
-void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) { |
- alloc_info->top = p->ObjectAreaStart(); |
- alloc_info->limit = p->ObjectAreaEnd(); |
- ASSERT(alloc_info->VerifyPagedAllocation()); |
-} |
- |
- |
-void PagedSpace::MCResetRelocationInfo() { |
- // Set page indexes. |
- int i = 0; |
- PageIterator it(this, PageIterator::ALL_PAGES); |
- while (it.has_next()) { |
- Page* p = it.next(); |
- p->mc_page_index = i++; |
- } |
- |
- // Set mc_forwarding_info_ to the first page in the space. |
- SetAllocationInfo(&mc_forwarding_info_, first_page_); |
- // All the bytes in the space are 'available'. We will rediscover |
- // allocated and wasted bytes during GC. |
- accounting_stats_.Reset(); |
-} |
- |
- |
-int PagedSpace::MCSpaceOffsetForAddress(Address addr) { |
-#ifdef DEBUG |
- // The Contains function considers the address at the beginning of a |
- // page in the page, MCSpaceOffsetForAddress considers it is in the |
- // previous page. |
- if (Page::IsAlignedToPageSize(addr)) { |
- ASSERT(Contains(addr - kPointerSize)); |
- } else { |
- ASSERT(Contains(addr)); |
- } |
-#endif |
- |
- // If addr is at the end of a page, it belongs to previous page |
- Page* p = Page::IsAlignedToPageSize(addr) |
- ? Page::FromAllocationTop(addr) |
- : Page::FromAddress(addr); |
- int index = p->mc_page_index; |
- return (index * Page::kPageSize) + p->Offset(addr); |
-} |
- |
- |
-// Slow case for reallocating and promoting objects during a compacting |
-// collection. This function is not space-specific. |
-HeapObject* PagedSpace::SlowMCAllocateRaw(int size_in_bytes) { |
- Page* current_page = TopPageOf(mc_forwarding_info_); |
- if (!current_page->next_page()->is_valid()) { |
- if (!Expand(current_page)) { |
- return NULL; |
- } |
- } |
- |
- // There are surely more pages in the space now. |
- ASSERT(current_page->next_page()->is_valid()); |
- // We do not add the top of page block for current page to the space's |
- // free list---the block may contain live objects so we cannot write |
- // bookkeeping information to it. Instead, we will recover top of page |
- // blocks when we move objects to their new locations. |
- // |
- // We do however write the allocation pointer to the page. The encoding |
- // of forwarding addresses is as an offset in terms of live bytes, so we |
- // need quick access to the allocation top of each page to decode |
- // forwarding addresses. |
- current_page->SetAllocationWatermark(mc_forwarding_info_.top); |
- current_page->next_page()->InvalidateWatermark(true); |
- SetAllocationInfo(&mc_forwarding_info_, current_page->next_page()); |
- return AllocateLinearly(&mc_forwarding_info_, size_in_bytes); |
-} |
- |
- |
-bool PagedSpace::Expand(Page* last_page) { |
+bool PagedSpace::CanExpand() { |
ASSERT(max_capacity_ % Page::kObjectAreaSize == 0); |
ASSERT(Capacity() % Page::kObjectAreaSize == 0); |
if (Capacity() == max_capacity_) return false; |
ASSERT(Capacity() < max_capacity_); |
- // Last page must be valid and its next page is invalid. |
- ASSERT(last_page->is_valid() && !last_page->next_page()->is_valid()); |
- int available_pages = |
- static_cast<int>((max_capacity_ - Capacity()) / Page::kObjectAreaSize); |
- // We don't want to have to handle small chunks near the end so if there are |
- // not kPagesPerChunk pages available without exceeding the max capacity then |
- // act as if memory has run out. |
- if (available_pages < MemoryAllocator::kPagesPerChunk) return false; |
+ // Are we going to exceed capacity for this space? |
+ if ((Capacity() + Page::kPageSize) > max_capacity_) return false; |
- int desired_pages = Min(available_pages, MemoryAllocator::kPagesPerChunk); |
- Page* p = heap()->isolate()->memory_allocator()->AllocatePages( |
- desired_pages, &desired_pages, this); |
- if (!p->is_valid()) return false; |
+ return true; |
+} |
- accounting_stats_.ExpandSpace(desired_pages * Page::kObjectAreaSize); |
- ASSERT(Capacity() <= max_capacity_); |
+bool PagedSpace::Expand() { |
+ if (!CanExpand()) return false; |
- heap()->isolate()->memory_allocator()->SetNextPage(last_page, p); |
+ Page* p = heap()->isolate()->memory_allocator()-> |
+ AllocatePage(this, executable()); |
+ if (p == NULL) return false; |
- // Sequentially clear region marks of new pages and and cache the |
- // new last page in the space. |
- while (p->is_valid()) { |
- p->SetRegionMarks(Page::kAllRegionsCleanMarks); |
- last_page_ = p; |
- p = p->next_page(); |
- } |
+ ASSERT(Capacity() <= max_capacity_); |
+ p->InsertAfter(anchor_.prev_page()); |
+ |
return true; |
} |
#ifdef DEBUG |
int PagedSpace::CountTotalPages() { |
+ PageIterator it(this); |
int count = 0; |
- for (Page* p = first_page_; p->is_valid(); p = p->next_page()) { |
+ while (it.has_next()) { |
+ it.next(); |
count++; |
} |
return count; |
@@ -1049,61 +760,15 @@ |
void PagedSpace::Shrink() { |
- if (!page_list_is_chunk_ordered_) { |
- // We can't shrink space if pages is not chunk-ordered |
- // (see comment for class MemoryAllocator for definition). |
- return; |
- } |
- |
- // Release half of free pages. |
- Page* top_page = AllocationTopPage(); |
- ASSERT(top_page->is_valid()); |
- |
- // Count the number of pages we would like to free. |
- int pages_to_free = 0; |
- for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) { |
- pages_to_free++; |
- } |
- |
- // Free pages after top_page. |
- Page* p = heap()->isolate()->memory_allocator()-> |
- FreePages(top_page->next_page()); |
- heap()->isolate()->memory_allocator()->SetNextPage(top_page, p); |
- |
- // Find out how many pages we failed to free and update last_page_. |
- // Please note pages can only be freed in whole chunks. |
- last_page_ = top_page; |
- for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) { |
- pages_to_free--; |
- last_page_ = p; |
- } |
- |
- accounting_stats_.ShrinkSpace(pages_to_free * Page::kObjectAreaSize); |
- ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize); |
+ // TODO(1614) Not implemented. |
} |
bool PagedSpace::EnsureCapacity(int capacity) { |
- if (Capacity() >= capacity) return true; |
- |
- // Start from the allocation top and loop to the last page in the space. |
- Page* last_page = AllocationTopPage(); |
- Page* next_page = last_page->next_page(); |
- while (next_page->is_valid()) { |
- last_page = heap()->isolate()->memory_allocator()-> |
- FindLastPageInSameChunk(next_page); |
- next_page = last_page->next_page(); |
+ while (Capacity() < capacity) { |
+ // Expand the space until it has the required capacity or expansion fails. |
+ if (!Expand()) return false; |
} |
- |
- // Expand the space until it has the required capacity or expansion fails. |
- do { |
- if (!Expand(last_page)) return false; |
- ASSERT(last_page->next_page()->is_valid()); |
- last_page = |
- heap()->isolate()->memory_allocator()->FindLastPageInSameChunk( |
- last_page->next_page()); |
- } while (Capacity() < capacity); |
- |
return true; |
} |
@@ -1114,60 +779,51 @@ |
#ifdef DEBUG |
-// We do not assume that the PageIterator works, because it depends on the |
-// invariants we are checking during verification. |
void PagedSpace::Verify(ObjectVisitor* visitor) { |
- // The allocation pointer should be valid, and it should be in a page in the |
- // space. |
- ASSERT(allocation_info_.VerifyPagedAllocation()); |
- Page* top_page = Page::FromAllocationTop(allocation_info_.top); |
- ASSERT(heap()->isolate()->memory_allocator()->IsPageInSpace(top_page, this)); |
+ // We can only iterate over the pages if they were swept precisely. |
+ if (was_swept_conservatively_) return; |
- // Loop over all the pages. |
- bool above_allocation_top = false; |
- Page* current_page = first_page_; |
- while (current_page->is_valid()) { |
- if (above_allocation_top) { |
- // We don't care what's above the allocation top. |
- } else { |
- Address top = current_page->AllocationTop(); |
- if (current_page == top_page) { |
- ASSERT(top == allocation_info_.top); |
- // The next page will be above the allocation top. |
- above_allocation_top = true; |
- } |
+ bool allocation_pointer_found_in_space = |
+ (allocation_info_.top != allocation_info_.limit); |
+ PageIterator page_iterator(this); |
+ while (page_iterator.has_next()) { |
+ Page* page = page_iterator.next(); |
+ ASSERT(page->owner() == this); |
+ if (page == Page::FromAllocationTop(allocation_info_.top)) { |
+ allocation_pointer_found_in_space = true; |
+ } |
+ ASSERT(page->WasSweptPrecisely()); |
+ HeapObjectIterator it(page, NULL); |
+ Address end_of_previous_object = page->ObjectAreaStart(); |
+ Address top = page->ObjectAreaEnd(); |
+ int black_size = 0; |
+ for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) { |
+ ASSERT(end_of_previous_object <= object->address()); |
- // It should be packed with objects from the bottom to the top. |
- Address current = current_page->ObjectAreaStart(); |
- while (current < top) { |
- HeapObject* object = HeapObject::FromAddress(current); |
+ // The first word should be a map, and we expect all map pointers to |
+ // be in map space. |
+ Map* map = object->map(); |
+ ASSERT(map->IsMap()); |
+ ASSERT(heap()->map_space()->Contains(map)); |
- // The first word should be a map, and we expect all map pointers to |
- // be in map space. |
- Map* map = object->map(); |
- ASSERT(map->IsMap()); |
- ASSERT(heap()->map_space()->Contains(map)); |
+ // Perform space-specific object verification. |
+ VerifyObject(object); |
- // Perform space-specific object verification. |
- VerifyObject(object); |
+ // The object itself should look OK. |
+ object->Verify(); |
- // The object itself should look OK. |
- object->Verify(); |
- |
- // All the interior pointers should be contained in the heap and |
- // have page regions covering intergenerational references should be |
- // marked dirty. |
- int size = object->Size(); |
- object->IterateBody(map->instance_type(), size, visitor); |
- |
- current += size; |
+ // All the interior pointers should be contained in the heap. |
+ int size = object->Size(); |
+ object->IterateBody(map->instance_type(), size, visitor); |
+ if (Marking::IsBlack(Marking::MarkBitFrom(object))) { |
+ black_size += size; |
} |
- // The allocation pointer should not be in the middle of an object. |
- ASSERT(current == top); |
+ ASSERT(object->address() + size <= top); |
+ end_of_previous_object = object->address() + size; |
} |
- |
- current_page = current_page->next_page(); |
+ // TODO(1672): Page live bytes are off for some tests. |
+ // CHECK_LE(black_size, page->LiveBytes()); |
} |
} |
#endif |
@@ -1177,14 +833,24 @@ |
// NewSpace implementation |
-bool NewSpace::Setup(Address start, int size) { |
+bool NewSpace::Setup(int reserved_semispace_capacity, |
+ int maximum_semispace_capacity) { |
// Setup new space based on the preallocated memory block defined by |
// start and size. The provided space is divided into two semi-spaces. |
// To support fast containment testing in the new space, the size of |
// this chunk must be a power of two and it must be aligned to its size. |
int initial_semispace_capacity = heap()->InitialSemiSpaceSize(); |
- int maximum_semispace_capacity = heap()->MaxSemiSpaceSize(); |
+ size_t size = 2 * reserved_semispace_capacity; |
+ Address base = |
+ heap()->isolate()->memory_allocator()->ReserveAlignedMemory( |
+ size, size, &reservation_); |
+ if (base == NULL) return false; |
+ |
+ chunk_base_ = base; |
+ chunk_size_ = static_cast<uintptr_t>(size); |
+ LOG(heap()->isolate(), NewEvent("InitialChunk", chunk_base_, chunk_size_)); |
+ |
ASSERT(initial_semispace_capacity <= maximum_semispace_capacity); |
ASSERT(IsPowerOf2(maximum_semispace_capacity)); |
@@ -1197,31 +863,29 @@ |
INSTANCE_TYPE_LIST(SET_NAME) |
#undef SET_NAME |
- ASSERT(size == 2 * heap()->ReservedSemiSpaceSize()); |
- ASSERT(IsAddressAligned(start, size, 0)); |
+ ASSERT(reserved_semispace_capacity == heap()->ReservedSemiSpaceSize()); |
+ ASSERT(static_cast<intptr_t>(chunk_size_) >= |
+ 2 * heap()->ReservedSemiSpaceSize()); |
+ ASSERT(IsAddressAligned(chunk_base_, 2 * reserved_semispace_capacity, 0)); |
- if (!to_space_.Setup(start, |
+ if (!to_space_.Setup(chunk_base_, |
initial_semispace_capacity, |
maximum_semispace_capacity)) { |
return false; |
} |
- if (!from_space_.Setup(start + maximum_semispace_capacity, |
+ if (!from_space_.Setup(chunk_base_ + reserved_semispace_capacity, |
initial_semispace_capacity, |
maximum_semispace_capacity)) { |
return false; |
} |
- start_ = start; |
- address_mask_ = ~(size - 1); |
+ start_ = chunk_base_; |
+ address_mask_ = ~(2 * reserved_semispace_capacity - 1); |
object_mask_ = address_mask_ | kHeapObjectTagMask; |
- object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag; |
+ object_expected_ = reinterpret_cast<uintptr_t>(start_) | kHeapObjectTag; |
- allocation_info_.top = to_space_.low(); |
- allocation_info_.limit = to_space_.high(); |
- mc_forwarding_info_.top = NULL; |
- mc_forwarding_info_.limit = NULL; |
+ ResetAllocationInfo(); |
- ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); |
return true; |
} |
@@ -1239,18 +903,22 @@ |
start_ = NULL; |
allocation_info_.top = NULL; |
allocation_info_.limit = NULL; |
- mc_forwarding_info_.top = NULL; |
- mc_forwarding_info_.limit = NULL; |
to_space_.TearDown(); |
from_space_.TearDown(); |
+ |
+ LOG(heap()->isolate(), DeleteEvent("InitialChunk", chunk_base_)); |
+ |
+ ASSERT(reservation_.IsReserved()); |
+ heap()->isolate()->memory_allocator()->FreeMemory(&reservation_, |
+ NOT_EXECUTABLE); |
+ chunk_base_ = NULL; |
+ chunk_size_ = 0; |
} |
void NewSpace::Flip() { |
- SemiSpace tmp = from_space_; |
- from_space_ = to_space_; |
- to_space_ = tmp; |
+ SemiSpace::Swap(&from_space_, &to_space_); |
} |
@@ -1268,7 +936,6 @@ |
} |
} |
} |
- allocation_info_.limit = to_space_.high(); |
ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); |
} |
@@ -1290,36 +957,65 @@ |
} |
} |
} |
- allocation_info_.limit = to_space_.high(); |
+ allocation_info_.limit = to_space_.page_high(); |
ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); |
} |
-void NewSpace::ResetAllocationInfo() { |
- allocation_info_.top = to_space_.low(); |
- allocation_info_.limit = to_space_.high(); |
+void NewSpace::UpdateAllocationInfo() { |
+ allocation_info_.top = to_space_.page_low(); |
+ allocation_info_.limit = to_space_.page_high(); |
+ |
+ // Lower limit during incremental marking. |
+ if (heap()->incremental_marking()->IsMarking() && |
+ inline_allocation_limit_step() != 0) { |
+ Address new_limit = |
+ allocation_info_.top + inline_allocation_limit_step(); |
+ allocation_info_.limit = Min(new_limit, allocation_info_.limit); |
+ } |
ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); |
} |
-void NewSpace::MCResetRelocationInfo() { |
- mc_forwarding_info_.top = from_space_.low(); |
- mc_forwarding_info_.limit = from_space_.high(); |
- ASSERT_SEMISPACE_ALLOCATION_INFO(mc_forwarding_info_, from_space_); |
+void NewSpace::ResetAllocationInfo() { |
+ to_space_.Reset(); |
+ UpdateAllocationInfo(); |
+ pages_used_ = 0; |
+ // Clear all mark-bits in the to-space. |
+ NewSpacePageIterator it(&to_space_); |
+ while (it.has_next()) { |
+ Bitmap::Clear(it.next()); |
+ } |
} |
-void NewSpace::MCCommitRelocationInfo() { |
- // Assumes that the spaces have been flipped so that mc_forwarding_info_ is |
- // valid allocation info for the to space. |
- allocation_info_.top = mc_forwarding_info_.top; |
- allocation_info_.limit = to_space_.high(); |
- ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); |
+bool NewSpace::AddFreshPage() { |
+ Address top = allocation_info_.top; |
+ if (NewSpacePage::IsAtStart(top)) { |
+ // The current page is already empty. Don't try to make another. |
+ |
+ // We should only get here if someone asks to allocate more |
+ // than what can be stored in a single page. |
+ // TODO(gc): Change the limit on new-space allocation to prevent this |
+ // from happening (all such allocations should go directly to LOSpace). |
+ return false; |
+ } |
+ if (!to_space_.AdvancePage()) { |
+ // Failed to get a new page in to-space. |
+ return false; |
+ } |
+ // Clear remainder of current page. |
+ int remaining_in_page = |
+ static_cast<int>(NewSpacePage::FromLimit(top)->body_limit() - top); |
+ heap()->CreateFillerObjectAt(top, remaining_in_page); |
+ pages_used_++; |
+ UpdateAllocationInfo(); |
+ return true; |
} |
#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). |
void NewSpace::Verify() { |
// The allocation pointer should be in the space or at the very end. |
@@ -1327,59 +1023,53 @@ |
// There should be objects packed in from the low address up to the |
// allocation pointer. |
- Address current = to_space_.low(); |
- while (current < top()) { |
- HeapObject* object = HeapObject::FromAddress(current); |
+ Address current = to_space_.first_page()->body(); |
+ CHECK_EQ(current, to_space_.space_start()); |
- // The first word should be a map, and we expect all map pointers to |
- // be in map space. |
- Map* map = object->map(); |
- ASSERT(map->IsMap()); |
- ASSERT(heap()->map_space()->Contains(map)); |
+ while (current != top()) { |
+ if (!NewSpacePage::IsAtEnd(current)) { |
+ // The allocation pointer should not be in the middle of an object. |
+ CHECK(!NewSpacePage::FromLimit(current)->ContainsLimit(top()) || |
+ current < top()); |
- // The object should not be code or a map. |
- ASSERT(!object->IsMap()); |
- ASSERT(!object->IsCode()); |
+ HeapObject* object = HeapObject::FromAddress(current); |
- // The object itself should look OK. |
- object->Verify(); |
+ // The first word should be a map, and we expect all map pointers to |
+ // be in map space. |
+ Map* map = object->map(); |
+ CHECK(map->IsMap()); |
+ CHECK(heap()->map_space()->Contains(map)); |
- // All the interior pointers should be contained in the heap. |
- VerifyPointersVisitor visitor; |
- int size = object->Size(); |
- object->IterateBody(map->instance_type(), size, &visitor); |
+ // The object should not be code or a map. |
+ CHECK(!object->IsMap()); |
+ CHECK(!object->IsCode()); |
- current += size; |
- } |
+ // The object itself should look OK. |
+ object->Verify(); |
- // The allocation pointer should not be in the middle of an object. |
- ASSERT(current == top()); |
-} |
-#endif |
+ // All the interior pointers should be contained in the heap. |
+ VerifyPointersVisitor visitor; |
+ int size = object->Size(); |
+ object->IterateBody(map->instance_type(), size, &visitor); |
- |
-bool SemiSpace::Commit() { |
- ASSERT(!is_committed()); |
- if (!heap()->isolate()->memory_allocator()->CommitBlock( |
- start_, capacity_, executable())) { |
- return false; |
+ current += size; |
+ } else { |
+ // At end of page, switch to next page. |
+ NewSpacePage* page = NewSpacePage::FromLimit(current)->next_page(); |
+ // Next page should be valid. |
+ CHECK(!page->is_anchor()); |
+ current = page->body(); |
+ } |
} |
- committed_ = true; |
- return true; |
-} |
- |
-bool SemiSpace::Uncommit() { |
- ASSERT(is_committed()); |
- if (!heap()->isolate()->memory_allocator()->UncommitBlock( |
- start_, capacity_)) { |
- return false; |
- } |
- committed_ = false; |
- return true; |
+ // Check semi-spaces. |
+ ASSERT_EQ(from_space_.id(), kFromSpace); |
+ ASSERT_EQ(to_space_.id(), kToSpace); |
+ from_space_.Verify(); |
+ to_space_.Verify(); |
} |
+#endif |
- |
// ----------------------------------------------------------------------------- |
// SemiSpace implementation |
@@ -1392,11 +1082,11 @@ |
// otherwise. In the mark-compact collector, the memory region of the from |
// space is used as the marking stack. It requires contiguous memory |
// addresses. |
- initial_capacity_ = initial_capacity; |
+ ASSERT(maximum_capacity >= Page::kPageSize); |
+ initial_capacity_ = RoundDown(initial_capacity, Page::kPageSize); |
capacity_ = initial_capacity; |
- maximum_capacity_ = maximum_capacity; |
+ maximum_capacity_ = RoundDown(maximum_capacity, Page::kPageSize); |
committed_ = false; |
- |
start_ = start; |
address_mask_ = ~(maximum_capacity - 1); |
object_mask_ = address_mask_ | kHeapObjectTagMask; |
@@ -1413,81 +1103,267 @@ |
} |
-bool SemiSpace::Grow() { |
- // Double the semispace size but only up to maximum capacity. |
- int maximum_extra = maximum_capacity_ - capacity_; |
- int extra = Min(RoundUp(capacity_, static_cast<int>(OS::AllocateAlignment())), |
- maximum_extra); |
- if (!heap()->isolate()->memory_allocator()->CommitBlock( |
- high(), extra, executable())) { |
+bool SemiSpace::Commit() { |
+ ASSERT(!is_committed()); |
+ int pages = capacity_ / Page::kPageSize; |
+ Address end = start_ + maximum_capacity_; |
+ Address start = end - pages * Page::kPageSize; |
+ if (!heap()->isolate()->memory_allocator()->CommitBlock(start, |
+ capacity_, |
+ executable())) { |
return false; |
} |
- capacity_ += extra; |
+ |
+ NewSpacePage* page = anchor(); |
+ for (int i = 1; i <= pages; i++) { |
+ NewSpacePage* new_page = |
+ NewSpacePage::Initialize(heap(), end - i * Page::kPageSize, this); |
+ new_page->InsertAfter(page); |
+ page = new_page; |
+ } |
+ |
+ committed_ = true; |
+ Reset(); |
return true; |
} |
+bool SemiSpace::Uncommit() { |
+ ASSERT(is_committed()); |
+ Address start = start_ + maximum_capacity_ - capacity_; |
+ if (!heap()->isolate()->memory_allocator()->UncommitBlock(start, capacity_)) { |
+ return false; |
+ } |
+ anchor()->set_next_page(anchor()); |
+ anchor()->set_prev_page(anchor()); |
+ |
+ committed_ = false; |
+ return true; |
+} |
+ |
+ |
+bool SemiSpace::Grow() { |
+ // Double the semispace size but only up to maximum capacity. |
+ ASSERT(static_cast<size_t>(Page::kPageSize) > OS::AllocateAlignment()); |
+ int new_capacity = Min(maximum_capacity_, |
+ RoundUp(capacity_ * 2, static_cast<int>(Page::kPageSize))); |
+ return GrowTo(new_capacity); |
+} |
+ |
+ |
bool SemiSpace::GrowTo(int new_capacity) { |
+ ASSERT((new_capacity & Page::kPageAlignmentMask) == 0); |
ASSERT(new_capacity <= maximum_capacity_); |
ASSERT(new_capacity > capacity_); |
+ int pages_before = capacity_ / Page::kPageSize; |
+ int pages_after = new_capacity / Page::kPageSize; |
+ |
+ Address end = start_ + maximum_capacity_; |
+ Address start = end - new_capacity; |
size_t delta = new_capacity - capacity_; |
+ |
ASSERT(IsAligned(delta, OS::AllocateAlignment())); |
if (!heap()->isolate()->memory_allocator()->CommitBlock( |
- high(), delta, executable())) { |
+ start, delta, executable())) { |
return false; |
} |
capacity_ = new_capacity; |
+ NewSpacePage* last_page = anchor()->prev_page(); |
+ ASSERT(last_page != anchor()); |
+ for (int i = pages_before + 1; i <= pages_after; i++) { |
+ Address page_address = end - i * Page::kPageSize; |
+ NewSpacePage* new_page = NewSpacePage::Initialize(heap(), |
+ page_address, |
+ this); |
+ new_page->InsertAfter(last_page); |
+ Bitmap::Clear(new_page); |
+ // Duplicate the flags that was set on the old page. |
+ new_page->SetFlags(last_page->GetFlags(), |
+ NewSpacePage::kCopyOnFlipFlagsMask); |
+ last_page = new_page; |
+ } |
return true; |
} |
bool SemiSpace::ShrinkTo(int new_capacity) { |
+ ASSERT((new_capacity & Page::kPageAlignmentMask) == 0); |
ASSERT(new_capacity >= initial_capacity_); |
ASSERT(new_capacity < capacity_); |
+ // Semispaces grow backwards from the end of their allocated capacity, |
+ // so we find the before and after start addresses relative to the |
+ // end of the space. |
+ Address space_end = start_ + maximum_capacity_; |
+ Address old_start = space_end - capacity_; |
size_t delta = capacity_ - new_capacity; |
ASSERT(IsAligned(delta, OS::AllocateAlignment())); |
- if (!heap()->isolate()->memory_allocator()->UncommitBlock( |
- high() - delta, delta)) { |
+ if (!heap()->isolate()->memory_allocator()->UncommitBlock(old_start, delta)) { |
return false; |
} |
capacity_ = new_capacity; |
+ |
+ int pages_after = capacity_ / Page::kPageSize; |
+ NewSpacePage* new_last_page = |
+ NewSpacePage::FromAddress(space_end - pages_after * Page::kPageSize); |
+ new_last_page->set_next_page(anchor()); |
+ anchor()->set_prev_page(new_last_page); |
+ ASSERT(current_page_ == first_page()); |
+ |
return true; |
} |
+void SemiSpace::FlipPages(intptr_t flags, intptr_t mask) { |
+ anchor_.set_owner(this); |
+ // Fixup back-pointers to anchor. Address of anchor changes |
+ // when we swap. |
+ anchor_.prev_page()->set_next_page(&anchor_); |
+ anchor_.next_page()->set_prev_page(&anchor_); |
+ |
+ bool becomes_to_space = (id_ == kFromSpace); |
+ id_ = becomes_to_space ? kToSpace : kFromSpace; |
+ NewSpacePage* page = anchor_.next_page(); |
+ while (page != &anchor_) { |
+ page->set_owner(this); |
+ page->SetFlags(flags, mask); |
+ if (becomes_to_space) { |
+ page->ClearFlag(MemoryChunk::IN_FROM_SPACE); |
+ page->SetFlag(MemoryChunk::IN_TO_SPACE); |
+ page->ClearFlag(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK); |
+ page->ResetLiveBytes(); |
+ } else { |
+ page->SetFlag(MemoryChunk::IN_FROM_SPACE); |
+ page->ClearFlag(MemoryChunk::IN_TO_SPACE); |
+ } |
+ ASSERT(page->IsFlagSet(MemoryChunk::SCAN_ON_SCAVENGE)); |
+ ASSERT(page->IsFlagSet(MemoryChunk::IN_TO_SPACE) || |
+ page->IsFlagSet(MemoryChunk::IN_FROM_SPACE)); |
+ page = page->next_page(); |
+ } |
+} |
+ |
+ |
+void SemiSpace::Reset() { |
+ ASSERT(anchor_.next_page() != &anchor_); |
+ current_page_ = anchor_.next_page(); |
+} |
+ |
+ |
+void SemiSpace::Swap(SemiSpace* from, SemiSpace* to) { |
+ // We won't be swapping semispaces without data in them. |
+ ASSERT(from->anchor_.next_page() != &from->anchor_); |
+ ASSERT(to->anchor_.next_page() != &to->anchor_); |
+ |
+ // Swap bits. |
+ SemiSpace tmp = *from; |
+ *from = *to; |
+ *to = tmp; |
+ |
+ // Fixup back-pointers to the page list anchor now that its address |
+ // has changed. |
+ // Swap to/from-space bits on pages. |
+ // Copy GC flags from old active space (from-space) to new (to-space). |
+ intptr_t flags = from->current_page()->GetFlags(); |
+ to->FlipPages(flags, NewSpacePage::kCopyOnFlipFlagsMask); |
+ |
+ from->FlipPages(0, 0); |
+} |
+ |
+ |
+void SemiSpace::set_age_mark(Address mark) { |
+ ASSERT(NewSpacePage::FromLimit(mark)->semi_space() == this); |
+ age_mark_ = mark; |
+ // Mark all pages up to the one containing mark. |
+ NewSpacePageIterator it(space_start(), mark); |
+ while (it.has_next()) { |
+ it.next()->SetFlag(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK); |
+ } |
+} |
+ |
+ |
#ifdef DEBUG |
void SemiSpace::Print() { } |
-void SemiSpace::Verify() { } |
+void SemiSpace::Verify() { |
+ bool is_from_space = (id_ == kFromSpace); |
+ NewSpacePage* page = anchor_.next_page(); |
+ CHECK(anchor_.semi_space() == this); |
+ while (page != &anchor_) { |
+ CHECK(page->semi_space() == this); |
+ CHECK(page->InNewSpace()); |
+ CHECK(page->IsFlagSet(is_from_space ? MemoryChunk::IN_FROM_SPACE |
+ : MemoryChunk::IN_TO_SPACE)); |
+ CHECK(!page->IsFlagSet(is_from_space ? MemoryChunk::IN_TO_SPACE |
+ : MemoryChunk::IN_FROM_SPACE)); |
+ CHECK(page->IsFlagSet(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING)); |
+ if (!is_from_space) { |
+ // The pointers-from-here-are-interesting flag isn't updated dynamically |
+ // on from-space pages, so it might be out of sync with the marking state. |
+ if (page->heap()->incremental_marking()->IsMarking()) { |
+ CHECK(page->IsFlagSet(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING)); |
+ } else { |
+ CHECK(!page->IsFlagSet( |
+ MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING)); |
+ } |
+ // TODO(gc): Check that the live_bytes_count_ field matches the |
+ // black marking on the page (if we make it match in new-space). |
+ } |
+ CHECK(page->IsFlagSet(MemoryChunk::SCAN_ON_SCAVENGE)); |
+ CHECK(page->prev_page()->next_page() == page); |
+ page = page->next_page(); |
+ } |
+} |
+ |
+ |
+void SemiSpace::AssertValidRange(Address start, Address end) { |
+ // Addresses belong to same semi-space |
+ NewSpacePage* page = NewSpacePage::FromAddress(start); |
+ NewSpacePage* end_page = NewSpacePage::FromLimit(end); |
+ SemiSpace* space = page->semi_space(); |
+ CHECK_EQ(space, end_page->semi_space()); |
+ // Start address is before end address, either on same page, |
+ // or end address is on a later page in the linked list of |
+ // semi-space pages. |
+ if (page == end_page) { |
+ CHECK(start <= end); |
+ } else { |
+ while (page != end_page) { |
+ page = page->next_page(); |
+ CHECK_NE(page, space->anchor()); |
+ } |
+ } |
+} |
#endif |
// ----------------------------------------------------------------------------- |
// SemiSpaceIterator implementation. |
SemiSpaceIterator::SemiSpaceIterator(NewSpace* space) { |
- Initialize(space, space->bottom(), space->top(), NULL); |
+ Initialize(space->bottom(), space->top(), NULL); |
} |
SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, |
HeapObjectCallback size_func) { |
- Initialize(space, space->bottom(), space->top(), size_func); |
+ Initialize(space->bottom(), space->top(), size_func); |
} |
SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, Address start) { |
- Initialize(space, start, space->top(), NULL); |
+ Initialize(start, space->top(), NULL); |
} |
-void SemiSpaceIterator::Initialize(NewSpace* space, Address start, |
+SemiSpaceIterator::SemiSpaceIterator(Address from, Address to) { |
+ Initialize(from, to, NULL); |
+} |
+ |
+ |
+void SemiSpaceIterator::Initialize(Address start, |
Address end, |
HeapObjectCallback size_func) { |
- ASSERT(space->ToSpaceContains(start)); |
- ASSERT(space->ToSpaceLow() <= end |
- && end <= space->ToSpaceHigh()); |
- space_ = &space->to_space_; |
+ SemiSpace::AssertValidRange(start, end); |
current_ = start; |
limit_ = end; |
size_func_ = size_func; |
@@ -1623,7 +1499,7 @@ |
void NewSpace::CollectStatistics() { |
ClearHistograms(); |
SemiSpaceIterator it(this); |
- for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) |
+ for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) |
RecordAllocation(obj); |
} |
@@ -1699,7 +1575,6 @@ |
promoted_histogram_[type].increment_bytes(obj->Size()); |
} |
- |
// ----------------------------------------------------------------------------- |
// Free lists for old object spaces implementation |
@@ -1708,17 +1583,17 @@ |
ASSERT(IsAligned(size_in_bytes, kPointerSize)); |
// We write a map and possibly size information to the block. If the block |
- // is big enough to be a ByteArray with at least one extra word (the next |
- // pointer), we set its map to be the byte array map and its size to an |
+ // is big enough to be a FreeSpace with at least one extra word (the next |
+ // pointer), we set its map to be the free space map and its size to an |
// appropriate array length for the desired size from HeapObject::Size(). |
// If the block is too small (eg, one or two words), to hold both a size |
// field and a next pointer, we give it a filler map that gives it the |
// correct size. |
- if (size_in_bytes > ByteArray::kHeaderSize) { |
- set_map(heap->raw_unchecked_byte_array_map()); |
- // Can't use ByteArray::cast because it fails during deserialization. |
- ByteArray* this_as_byte_array = reinterpret_cast<ByteArray*>(this); |
- this_as_byte_array->set_length(ByteArray::LengthFor(size_in_bytes)); |
+ if (size_in_bytes > FreeSpace::kHeaderSize) { |
+ set_map(heap->raw_unchecked_free_space_map()); |
+ // Can't use FreeSpace::cast because it fails during deserialization. |
+ FreeSpace* this_as_free_space = reinterpret_cast<FreeSpace*>(this); |
+ this_as_free_space->set_size(size_in_bytes); |
} else if (size_in_bytes == kPointerSize) { |
set_map(heap->raw_unchecked_one_pointer_filler_map()); |
} else if (size_in_bytes == 2 * kPointerSize) { |
@@ -1727,319 +1602,315 @@ |
UNREACHABLE(); |
} |
// We would like to ASSERT(Size() == size_in_bytes) but this would fail during |
- // deserialization because the byte array map is not done yet. |
+ // deserialization because the free space map is not done yet. |
} |
-Address FreeListNode::next(Heap* heap) { |
+FreeListNode* FreeListNode::next() { |
ASSERT(IsFreeListNode(this)); |
- if (map() == heap->raw_unchecked_byte_array_map()) { |
- ASSERT(Size() >= kNextOffset + kPointerSize); |
- return Memory::Address_at(address() + kNextOffset); |
+ if (map() == HEAP->raw_unchecked_free_space_map()) { |
+ ASSERT(map() == NULL || Size() >= kNextOffset + kPointerSize); |
+ return reinterpret_cast<FreeListNode*>( |
+ Memory::Address_at(address() + kNextOffset)); |
} else { |
- return Memory::Address_at(address() + kPointerSize); |
+ return reinterpret_cast<FreeListNode*>( |
+ Memory::Address_at(address() + kPointerSize)); |
} |
} |
-void FreeListNode::set_next(Heap* heap, Address next) { |
+FreeListNode** FreeListNode::next_address() { |
ASSERT(IsFreeListNode(this)); |
- if (map() == heap->raw_unchecked_byte_array_map()) { |
+ if (map() == HEAP->raw_unchecked_free_space_map()) { |
ASSERT(Size() >= kNextOffset + kPointerSize); |
- Memory::Address_at(address() + kNextOffset) = next; |
+ return reinterpret_cast<FreeListNode**>(address() + kNextOffset); |
} else { |
- Memory::Address_at(address() + kPointerSize) = next; |
+ return reinterpret_cast<FreeListNode**>(address() + kPointerSize); |
} |
} |
-OldSpaceFreeList::OldSpaceFreeList(Heap* heap, AllocationSpace owner) |
- : heap_(heap), |
- owner_(owner) { |
+void FreeListNode::set_next(FreeListNode* next) { |
+ ASSERT(IsFreeListNode(this)); |
+ // While we are booting the VM the free space map will actually be null. So |
+ // we have to make sure that we don't try to use it for anything at that |
+ // stage. |
+ if (map() == HEAP->raw_unchecked_free_space_map()) { |
+ ASSERT(map() == NULL || Size() >= kNextOffset + kPointerSize); |
+ Memory::Address_at(address() + kNextOffset) = |
+ reinterpret_cast<Address>(next); |
+ } else { |
+ Memory::Address_at(address() + kPointerSize) = |
+ reinterpret_cast<Address>(next); |
+ } |
+} |
+ |
+ |
+FreeList::FreeList(PagedSpace* owner) |
+ : owner_(owner), heap_(owner->heap()) { |
Reset(); |
} |
-void OldSpaceFreeList::Reset() { |
+void FreeList::Reset() { |
available_ = 0; |
- for (int i = 0; i < kFreeListsLength; i++) { |
- free_[i].head_node_ = NULL; |
- } |
- needs_rebuild_ = false; |
- finger_ = kHead; |
- free_[kHead].next_size_ = kEnd; |
+ small_list_ = NULL; |
+ medium_list_ = NULL; |
+ large_list_ = NULL; |
+ huge_list_ = NULL; |
} |
-void OldSpaceFreeList::RebuildSizeList() { |
- ASSERT(needs_rebuild_); |
- int cur = kHead; |
- for (int i = cur + 1; i < kFreeListsLength; i++) { |
- if (free_[i].head_node_ != NULL) { |
- free_[cur].next_size_ = i; |
- cur = i; |
+int PagedSpace::FreeOrUnmapPage(Page* page, Address start, int size_in_bytes) { |
+ Heap* heap = page->heap(); |
+ // TODO(gc): When we count the live bytes per page we can free empty pages |
+ // instead of sweeping. At that point this if should be turned into an |
+ // ASSERT that the area to be freed cannot be the entire page. |
+ if (size_in_bytes == Page::kObjectAreaSize && |
+ heap->ShouldWeGiveBackAPageToTheOS()) { |
+ page->Unlink(); |
+ if (page->IsFlagSet(MemoryChunk::CONTAINS_ONLY_DATA)) { |
+ heap->isolate()->memory_allocator()->Free(page); |
+ } else { |
+ heap->QueueMemoryChunkForFree(page); |
} |
+ return 0; |
} |
- free_[cur].next_size_ = kEnd; |
- needs_rebuild_ = false; |
+ return Free(start, size_in_bytes); |
} |
-int OldSpaceFreeList::Free(Address start, int size_in_bytes) { |
-#ifdef DEBUG |
- Isolate::Current()->memory_allocator()->ZapBlock(start, size_in_bytes); |
-#endif |
+int FreeList::Free(Address start, int size_in_bytes) { |
+ if (size_in_bytes == 0) return 0; |
FreeListNode* node = FreeListNode::FromAddress(start); |
node->set_size(heap_, size_in_bytes); |
- // We don't use the freelists in compacting mode. This makes it more like a |
- // GC that only has mark-sweep-compact and doesn't have a mark-sweep |
- // collector. |
- if (FLAG_always_compact) { |
- return size_in_bytes; |
- } |
+ // Early return to drop too-small blocks on the floor. |
+ if (size_in_bytes < kSmallListMin) return size_in_bytes; |
- // Early return to drop too-small blocks on the floor (one or two word |
- // blocks cannot hold a map pointer, a size field, and a pointer to the |
- // next block in the free list). |
- if (size_in_bytes < kMinBlockSize) { |
- return size_in_bytes; |
+ // Insert other blocks at the head of a free list of the appropriate |
+ // magnitude. |
+ if (size_in_bytes <= kSmallListMax) { |
+ node->set_next(small_list_); |
+ small_list_ = node; |
+ } else if (size_in_bytes <= kMediumListMax) { |
+ node->set_next(medium_list_); |
+ medium_list_ = node; |
+ } else if (size_in_bytes <= kLargeListMax) { |
+ node->set_next(large_list_); |
+ large_list_ = node; |
+ } else { |
+ node->set_next(huge_list_); |
+ huge_list_ = node; |
} |
- |
- // Insert other blocks at the head of an exact free list. |
- int index = size_in_bytes >> kPointerSizeLog2; |
- node->set_next(heap_, free_[index].head_node_); |
- free_[index].head_node_ = node->address(); |
available_ += size_in_bytes; |
- needs_rebuild_ = true; |
+ ASSERT(IsVeryLong() || available_ == SumFreeLists()); |
return 0; |
} |
-MaybeObject* OldSpaceFreeList::Allocate(int size_in_bytes, int* wasted_bytes) { |
- ASSERT(0 < size_in_bytes); |
- ASSERT(size_in_bytes <= kMaxBlockSize); |
- ASSERT(IsAligned(size_in_bytes, kPointerSize)); |
+FreeListNode* FreeList::PickNodeFromList(FreeListNode** list, int* node_size) { |
+ FreeListNode* node = *list; |
- if (needs_rebuild_) RebuildSizeList(); |
- int index = size_in_bytes >> kPointerSizeLog2; |
- // Check for a perfect fit. |
- if (free_[index].head_node_ != NULL) { |
- FreeListNode* node = FreeListNode::FromAddress(free_[index].head_node_); |
- // If this was the last block of its size, remove the size. |
- if ((free_[index].head_node_ = node->next(heap_)) == NULL) |
- RemoveSize(index); |
- available_ -= size_in_bytes; |
- *wasted_bytes = 0; |
- ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. |
- return node; |
+ if (node == NULL) return NULL; |
+ |
+ while (node != NULL && |
+ Page::FromAddress(node->address())->IsEvacuationCandidate()) { |
+ available_ -= node->Size(); |
+ node = node->next(); |
} |
- // Search the size list for the best fit. |
- int prev = finger_ < index ? finger_ : kHead; |
- int cur = FindSize(index, &prev); |
- ASSERT(index < cur); |
- if (cur == kEnd) { |
- // No large enough size in list. |
- *wasted_bytes = 0; |
- return Failure::RetryAfterGC(owner_); |
- } |
- ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. |
- int rem = cur - index; |
- int rem_bytes = rem << kPointerSizeLog2; |
- FreeListNode* cur_node = FreeListNode::FromAddress(free_[cur].head_node_); |
- ASSERT(cur_node->Size() == (cur << kPointerSizeLog2)); |
- FreeListNode* rem_node = FreeListNode::FromAddress(free_[cur].head_node_ + |
- size_in_bytes); |
- // Distinguish the cases prev < rem < cur and rem <= prev < cur |
- // to avoid many redundant tests and calls to Insert/RemoveSize. |
- if (prev < rem) { |
- // Simple case: insert rem between prev and cur. |
- finger_ = prev; |
- free_[prev].next_size_ = rem; |
- // If this was the last block of size cur, remove the size. |
- if ((free_[cur].head_node_ = cur_node->next(heap_)) == NULL) { |
- free_[rem].next_size_ = free_[cur].next_size_; |
- } else { |
- free_[rem].next_size_ = cur; |
- } |
- // Add the remainder block. |
- rem_node->set_size(heap_, rem_bytes); |
- rem_node->set_next(heap_, free_[rem].head_node_); |
- free_[rem].head_node_ = rem_node->address(); |
+ |
+ if (node != NULL) { |
+ *node_size = node->Size(); |
+ *list = node->next(); |
} else { |
- // If this was the last block of size cur, remove the size. |
- if ((free_[cur].head_node_ = cur_node->next(heap_)) == NULL) { |
- finger_ = prev; |
- free_[prev].next_size_ = free_[cur].next_size_; |
- } |
- if (rem_bytes < kMinBlockSize) { |
- // Too-small remainder is wasted. |
- rem_node->set_size(heap_, rem_bytes); |
- available_ -= size_in_bytes + rem_bytes; |
- *wasted_bytes = rem_bytes; |
- return cur_node; |
- } |
- // Add the remainder block and, if needed, insert its size. |
- rem_node->set_size(heap_, rem_bytes); |
- rem_node->set_next(heap_, free_[rem].head_node_); |
- free_[rem].head_node_ = rem_node->address(); |
- if (rem_node->next(heap_) == NULL) InsertSize(rem); |
+ *list = NULL; |
} |
- available_ -= size_in_bytes; |
- *wasted_bytes = 0; |
- return cur_node; |
+ |
+ return node; |
} |
-void OldSpaceFreeList::MarkNodes() { |
- for (int i = 0; i < kFreeListsLength; i++) { |
- Address cur_addr = free_[i].head_node_; |
- while (cur_addr != NULL) { |
- FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); |
- cur_addr = cur_node->next(heap_); |
- cur_node->SetMark(); |
- } |
+FreeListNode* FreeList::FindNodeFor(int size_in_bytes, int* node_size) { |
+ FreeListNode* node = NULL; |
+ |
+ if (size_in_bytes <= kSmallAllocationMax) { |
+ node = PickNodeFromList(&small_list_, node_size); |
+ if (node != NULL) return node; |
} |
-} |
+ if (size_in_bytes <= kMediumAllocationMax) { |
+ node = PickNodeFromList(&medium_list_, node_size); |
+ if (node != NULL) return node; |
+ } |
-#ifdef DEBUG |
-bool OldSpaceFreeList::Contains(FreeListNode* node) { |
- for (int i = 0; i < kFreeListsLength; i++) { |
- Address cur_addr = free_[i].head_node_; |
- while (cur_addr != NULL) { |
- FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); |
- if (cur_node == node) return true; |
- cur_addr = cur_node->next(heap_); |
+ if (size_in_bytes <= kLargeAllocationMax) { |
+ node = PickNodeFromList(&large_list_, node_size); |
+ if (node != NULL) return node; |
+ } |
+ |
+ for (FreeListNode** cur = &huge_list_; |
+ *cur != NULL; |
+ cur = (*cur)->next_address()) { |
+ FreeListNode* cur_node = *cur; |
+ while (cur_node != NULL && |
+ Page::FromAddress(cur_node->address())->IsEvacuationCandidate()) { |
+ available_ -= reinterpret_cast<FreeSpace*>(cur_node)->Size(); |
+ cur_node = cur_node->next(); |
} |
+ |
+ *cur = cur_node; |
+ if (cur_node == NULL) break; |
+ |
+ ASSERT((*cur)->map() == HEAP->raw_unchecked_free_space_map()); |
+ FreeSpace* cur_as_free_space = reinterpret_cast<FreeSpace*>(*cur); |
+ int size = cur_as_free_space->Size(); |
+ if (size >= size_in_bytes) { |
+ // Large enough node found. Unlink it from the list. |
+ node = *cur; |
+ *node_size = size; |
+ *cur = node->next(); |
+ break; |
+ } |
} |
- return false; |
+ |
+ return node; |
} |
-#endif |
-FixedSizeFreeList::FixedSizeFreeList(Heap* heap, |
- AllocationSpace owner, |
- int object_size) |
- : heap_(heap), owner_(owner), object_size_(object_size) { |
- Reset(); |
-} |
+// Allocation on the old space free list. If it succeeds then a new linear |
+// allocation space has been set up with the top and limit of the space. If |
+// the allocation fails then NULL is returned, and the caller can perform a GC |
+// or allocate a new page before retrying. |
+HeapObject* FreeList::Allocate(int size_in_bytes) { |
+ ASSERT(0 < size_in_bytes); |
+ ASSERT(size_in_bytes <= kMaxBlockSize); |
+ ASSERT(IsAligned(size_in_bytes, kPointerSize)); |
+ // Don't free list allocate if there is linear space available. |
+ ASSERT(owner_->limit() - owner_->top() < size_in_bytes); |
+ int new_node_size = 0; |
+ FreeListNode* new_node = FindNodeFor(size_in_bytes, &new_node_size); |
+ if (new_node == NULL) return NULL; |
-void FixedSizeFreeList::Reset() { |
- available_ = 0; |
- head_ = tail_ = NULL; |
-} |
+ available_ -= new_node_size; |
+ ASSERT(IsVeryLong() || available_ == SumFreeLists()); |
+ int bytes_left = new_node_size - size_in_bytes; |
+ ASSERT(bytes_left >= 0); |
-void FixedSizeFreeList::Free(Address start) { |
-#ifdef DEBUG |
- Isolate::Current()->memory_allocator()->ZapBlock(start, object_size_); |
-#endif |
- // We only use the freelists with mark-sweep. |
- ASSERT(!HEAP->mark_compact_collector()->IsCompacting()); |
- FreeListNode* node = FreeListNode::FromAddress(start); |
- node->set_size(heap_, object_size_); |
- node->set_next(heap_, NULL); |
- if (head_ == NULL) { |
- tail_ = head_ = node->address(); |
+ int old_linear_size = static_cast<int>(owner_->limit() - owner_->top()); |
+ // Mark the old linear allocation area with a free space map so it can be |
+ // skipped when scanning the heap. This also puts it back in the free list |
+ // if it is big enough. |
+ owner_->Free(owner_->top(), old_linear_size); |
+ owner_->heap()->incremental_marking()->OldSpaceStep( |
+ size_in_bytes - old_linear_size); |
+ |
+ const int kThreshold = IncrementalMarking::kAllocatedThreshold; |
+ |
+ // Memory in the linear allocation area is counted as allocated. We may free |
+ // a little of this again immediately - see below. |
+ owner_->Allocate(new_node_size); |
+ |
+ if (bytes_left > kThreshold && |
+ owner_->heap()->incremental_marking()->IsMarkingIncomplete() && |
+ FLAG_incremental_marking_steps) { |
+ int linear_size = owner_->RoundSizeDownToObjectAlignment(kThreshold); |
+ // We don't want to give too large linear areas to the allocator while |
+ // incremental marking is going on, because we won't check again whether |
+ // we want to do another increment until the linear area is used up. |
+ owner_->Free(new_node->address() + size_in_bytes + linear_size, |
+ new_node_size - size_in_bytes - linear_size); |
+ owner_->SetTop(new_node->address() + size_in_bytes, |
+ new_node->address() + size_in_bytes + linear_size); |
+ } else if (bytes_left > 0) { |
+ // Normally we give the rest of the node to the allocator as its new |
+ // linear allocation area. |
+ owner_->SetTop(new_node->address() + size_in_bytes, |
+ new_node->address() + new_node_size); |
} else { |
- FreeListNode::FromAddress(tail_)->set_next(heap_, node->address()); |
- tail_ = node->address(); |
+ // TODO(gc) Try not freeing linear allocation region when bytes_left |
+ // are zero. |
+ owner_->SetTop(NULL, NULL); |
} |
- available_ += object_size_; |
+ |
+ return new_node; |
} |
-MaybeObject* FixedSizeFreeList::Allocate() { |
- if (head_ == NULL) { |
- return Failure::RetryAfterGC(owner_); |
+static intptr_t CountFreeListItemsInList(FreeListNode* n, Page* p) { |
+ intptr_t sum = 0; |
+ while (n != NULL) { |
+ if (Page::FromAddress(n->address()) == p) { |
+ FreeSpace* free_space = reinterpret_cast<FreeSpace*>(n); |
+ sum += free_space->Size(); |
+ } |
+ n = n->next(); |
} |
- |
- ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. |
- FreeListNode* node = FreeListNode::FromAddress(head_); |
- head_ = node->next(heap_); |
- available_ -= object_size_; |
- return node; |
+ return sum; |
} |
-void FixedSizeFreeList::MarkNodes() { |
- Address cur_addr = head_; |
- while (cur_addr != NULL && cur_addr != tail_) { |
- FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); |
- cur_addr = cur_node->next(heap_); |
- cur_node->SetMark(); |
+void FreeList::CountFreeListItems(Page* p, intptr_t* sizes) { |
+ sizes[0] = CountFreeListItemsInList(small_list_, p); |
+ sizes[1] = CountFreeListItemsInList(medium_list_, p); |
+ sizes[2] = CountFreeListItemsInList(large_list_, p); |
+ sizes[3] = CountFreeListItemsInList(huge_list_, p); |
+} |
+ |
+#ifdef DEBUG |
+intptr_t FreeList::SumFreeList(FreeListNode* cur) { |
+ intptr_t sum = 0; |
+ while (cur != NULL) { |
+ ASSERT(cur->map() == HEAP->raw_unchecked_free_space_map()); |
+ FreeSpace* cur_as_free_space = reinterpret_cast<FreeSpace*>(cur); |
+ sum += cur_as_free_space->Size(); |
+ cur = cur->next(); |
} |
+ return sum; |
} |
-// ----------------------------------------------------------------------------- |
-// OldSpace implementation |
+static const int kVeryLongFreeList = 500; |
-void OldSpace::PrepareForMarkCompact(bool will_compact) { |
- // Call prepare of the super class. |
- PagedSpace::PrepareForMarkCompact(will_compact); |
- if (will_compact) { |
- // Reset relocation info. During a compacting collection, everything in |
- // the space is considered 'available' and we will rediscover live data |
- // and waste during the collection. |
- MCResetRelocationInfo(); |
- ASSERT(Available() == Capacity()); |
- } else { |
- // During a non-compacting collection, everything below the linear |
- // allocation pointer is considered allocated (everything above is |
- // available) and we will rediscover available and wasted bytes during |
- // the collection. |
- accounting_stats_.AllocateBytes(free_list_.available()); |
- accounting_stats_.FillWastedBytes(Waste()); |
+int FreeList::FreeListLength(FreeListNode* cur) { |
+ int length = 0; |
+ while (cur != NULL) { |
+ length++; |
+ cur = cur->next(); |
+ if (length == kVeryLongFreeList) return length; |
} |
- |
- // Clear the free list before a full GC---it will be rebuilt afterward. |
- free_list_.Reset(); |
+ return length; |
} |
-void OldSpace::MCCommitRelocationInfo() { |
- // Update fast allocation info. |
- allocation_info_.top = mc_forwarding_info_.top; |
- allocation_info_.limit = mc_forwarding_info_.limit; |
- ASSERT(allocation_info_.VerifyPagedAllocation()); |
+bool FreeList::IsVeryLong() { |
+ if (FreeListLength(small_list_) == kVeryLongFreeList) return true; |
+ if (FreeListLength(medium_list_) == kVeryLongFreeList) return true; |
+ if (FreeListLength(large_list_) == kVeryLongFreeList) return true; |
+ if (FreeListLength(huge_list_) == kVeryLongFreeList) return true; |
+ return false; |
+} |
- // The space is compacted and we haven't yet built free lists or |
- // wasted any space. |
- ASSERT(Waste() == 0); |
- ASSERT(AvailableFree() == 0); |
- // Build the free list for the space. |
- int computed_size = 0; |
- PageIterator it(this, PageIterator::PAGES_USED_BY_MC); |
- while (it.has_next()) { |
- Page* p = it.next(); |
- // Space below the relocation pointer is allocated. |
- computed_size += |
- static_cast<int>(p->AllocationWatermark() - p->ObjectAreaStart()); |
- if (it.has_next()) { |
- // Free the space at the top of the page. |
- int extra_size = |
- static_cast<int>(p->ObjectAreaEnd() - p->AllocationWatermark()); |
- if (extra_size > 0) { |
- int wasted_bytes = free_list_.Free(p->AllocationWatermark(), |
- extra_size); |
- // The bytes we have just "freed" to add to the free list were |
- // already accounted as available. |
- accounting_stats_.WasteBytes(wasted_bytes); |
- } |
- } |
- } |
- |
- // Make sure the computed size - based on the used portion of the pages in |
- // use - matches the size obtained while computing forwarding addresses. |
- ASSERT(computed_size == Size()); |
+// This can take a very long time because it is linear in the number of entries |
+// on the free list, so it should not be called if FreeListLength returns |
+// kVeryLongFreeList. |
+intptr_t FreeList::SumFreeLists() { |
+ intptr_t sum = SumFreeList(small_list_); |
+ sum += SumFreeList(medium_list_); |
+ sum += SumFreeList(large_list_); |
+ sum += SumFreeList(huge_list_); |
+ return sum; |
} |
+#endif |
+// ----------------------------------------------------------------------------- |
+// OldSpace implementation |
+ |
bool NewSpace::ReserveSpace(int bytes) { |
// We can't reliably unpack a partial snapshot that needs more new space |
// space than the minimum NewSpace size. |
@@ -2050,201 +1921,118 @@ |
} |
-void PagedSpace::FreePages(Page* prev, Page* last) { |
- if (last == AllocationTopPage()) { |
- // Pages are already at the end of used pages. |
- return; |
- } |
+void PagedSpace::PrepareForMarkCompact() { |
+ // We don't have a linear allocation area while sweeping. It will be restored |
+ // on the first allocation after the sweep. |
+ // Mark the old linear allocation area with a free space map so it can be |
+ // skipped when scanning the heap. |
+ int old_linear_size = static_cast<int>(limit() - top()); |
+ Free(top(), old_linear_size); |
+ SetTop(NULL, NULL); |
- Page* first = NULL; |
- |
- // Remove pages from the list. |
- if (prev == NULL) { |
- first = first_page_; |
- first_page_ = last->next_page(); |
- } else { |
- first = prev->next_page(); |
- heap()->isolate()->memory_allocator()->SetNextPage( |
- prev, last->next_page()); |
+ // Stop lazy sweeping and clear marking bits for unswept pages. |
+ if (first_unswept_page_ != NULL) { |
+ Page* last = last_unswept_page_->next_page(); |
+ Page* p = first_unswept_page_; |
+ do { |
+ if (ShouldBeSweptLazily(p)) { |
+ ASSERT(!p->WasSwept()); |
+ Bitmap::Clear(p); |
+ if (FLAG_gc_verbose) { |
+ PrintF("Sweeping 0x%" V8PRIxPTR " lazily abandoned.\n", |
+ reinterpret_cast<intptr_t>(p)); |
+ } |
+ } |
+ p = p->next_page(); |
+ } while (p != last); |
} |
+ first_unswept_page_ = last_unswept_page_ = Page::FromAddress(NULL); |
- // Attach it after the last page. |
- heap()->isolate()->memory_allocator()->SetNextPage(last_page_, first); |
- last_page_ = last; |
- heap()->isolate()->memory_allocator()->SetNextPage(last, NULL); |
+ // Clear the free list before a full GC---it will be rebuilt afterward. |
+ free_list_.Reset(); |
+} |
- // Clean them up. |
- do { |
- first->InvalidateWatermark(true); |
- first->SetAllocationWatermark(first->ObjectAreaStart()); |
- first->SetCachedAllocationWatermark(first->ObjectAreaStart()); |
- first->SetRegionMarks(Page::kAllRegionsCleanMarks); |
- first = first->next_page(); |
- } while (first != NULL); |
- // Order of pages in this space might no longer be consistent with |
- // order of pages in chunks. |
- page_list_is_chunk_ordered_ = false; |
-} |
+bool PagedSpace::ReserveSpace(int size_in_bytes) { |
+ ASSERT(size_in_bytes <= Page::kMaxHeapObjectSize); |
+ ASSERT(size_in_bytes == RoundSizeDownToObjectAlignment(size_in_bytes)); |
+ Address current_top = allocation_info_.top; |
+ Address new_top = current_top + size_in_bytes; |
+ if (new_top <= allocation_info_.limit) return true; |
+ HeapObject* new_area = free_list_.Allocate(size_in_bytes); |
+ if (new_area == NULL) new_area = SlowAllocateRaw(size_in_bytes); |
+ if (new_area == NULL) return false; |
-void PagedSpace::RelinkPageListInChunkOrder(bool deallocate_blocks) { |
- const bool add_to_freelist = true; |
+ int old_linear_size = static_cast<int>(limit() - top()); |
+ // Mark the old linear allocation area with a free space so it can be |
+ // skipped when scanning the heap. This also puts it back in the free list |
+ // if it is big enough. |
+ Free(top(), old_linear_size); |
- // Mark used and unused pages to properly fill unused pages |
- // after reordering. |
- PageIterator all_pages_iterator(this, PageIterator::ALL_PAGES); |
- Page* last_in_use = AllocationTopPage(); |
- bool in_use = true; |
+ SetTop(new_area->address(), new_area->address() + size_in_bytes); |
+ Allocate(size_in_bytes); |
+ return true; |
+} |
- while (all_pages_iterator.has_next()) { |
- Page* p = all_pages_iterator.next(); |
- p->SetWasInUseBeforeMC(in_use); |
- if (p == last_in_use) { |
- // We passed a page containing allocation top. All consequent |
- // pages are not used. |
- in_use = false; |
- } |
- } |
- if (page_list_is_chunk_ordered_) return; |
+// You have to call this last, since the implementation from PagedSpace |
+// doesn't know that memory was 'promised' to large object space. |
+bool LargeObjectSpace::ReserveSpace(int bytes) { |
+ return heap()->OldGenerationSpaceAvailable() >= bytes; |
+} |
- Page* new_last_in_use = Page::FromAddress(NULL); |
- heap()->isolate()->memory_allocator()->RelinkPageListInChunkOrder( |
- this, &first_page_, &last_page_, &new_last_in_use); |
- ASSERT(new_last_in_use->is_valid()); |
- if (new_last_in_use != last_in_use) { |
- // Current allocation top points to a page which is now in the middle |
- // of page list. We should move allocation top forward to the new last |
- // used page so various object iterators will continue to work properly. |
- int size_in_bytes = static_cast<int>(PageAllocationLimit(last_in_use) - |
- last_in_use->AllocationTop()); |
+bool PagedSpace::AdvanceSweeper(intptr_t bytes_to_sweep) { |
+ if (IsSweepingComplete()) return true; |
- last_in_use->SetAllocationWatermark(last_in_use->AllocationTop()); |
- if (size_in_bytes > 0) { |
- Address start = last_in_use->AllocationTop(); |
- if (deallocate_blocks) { |
- accounting_stats_.AllocateBytes(size_in_bytes); |
- DeallocateBlock(start, size_in_bytes, add_to_freelist); |
- } else { |
- heap()->CreateFillerObjectAt(start, size_in_bytes); |
+ intptr_t freed_bytes = 0; |
+ Page* last = last_unswept_page_->next_page(); |
+ Page* p = first_unswept_page_; |
+ do { |
+ Page* next_page = p->next_page(); |
+ if (ShouldBeSweptLazily(p)) { |
+ if (FLAG_gc_verbose) { |
+ PrintF("Sweeping 0x%" V8PRIxPTR " lazily advanced.\n", |
+ reinterpret_cast<intptr_t>(p)); |
} |
+ freed_bytes += MarkCompactCollector::SweepConservatively(this, p); |
} |
+ p = next_page; |
+ } while (p != last && freed_bytes < bytes_to_sweep); |
- // New last in use page was in the middle of the list before |
- // sorting so it full. |
- SetTop(new_last_in_use->AllocationTop()); |
- |
- ASSERT(AllocationTopPage() == new_last_in_use); |
- ASSERT(AllocationTopPage()->WasInUseBeforeMC()); |
+ if (p == last) { |
+ last_unswept_page_ = first_unswept_page_ = Page::FromAddress(NULL); |
+ } else { |
+ first_unswept_page_ = p; |
} |
- PageIterator pages_in_use_iterator(this, PageIterator::PAGES_IN_USE); |
- while (pages_in_use_iterator.has_next()) { |
- Page* p = pages_in_use_iterator.next(); |
- if (!p->WasInUseBeforeMC()) { |
- // Empty page is in the middle of a sequence of used pages. |
- // Allocate it as a whole and deallocate immediately. |
- int size_in_bytes = static_cast<int>(PageAllocationLimit(p) - |
- p->ObjectAreaStart()); |
+ heap()->LowerOldGenLimits(freed_bytes); |
- p->SetAllocationWatermark(p->ObjectAreaStart()); |
- Address start = p->ObjectAreaStart(); |
- if (deallocate_blocks) { |
- accounting_stats_.AllocateBytes(size_in_bytes); |
- DeallocateBlock(start, size_in_bytes, add_to_freelist); |
- } else { |
- heap()->CreateFillerObjectAt(start, size_in_bytes); |
- } |
- } |
- } |
+ heap()->FreeQueuedChunks(); |
- page_list_is_chunk_ordered_ = true; |
+ return IsSweepingComplete(); |
} |
-void PagedSpace::PrepareForMarkCompact(bool will_compact) { |
- if (will_compact) { |
- RelinkPageListInChunkOrder(false); |
- } |
-} |
+void PagedSpace::EvictEvacuationCandidatesFromFreeLists() { |
+ if (allocation_info_.top >= allocation_info_.limit) return; |
+ if (Page::FromAddress(allocation_info_.top)->IsEvacuationCandidate()) { |
+ // Create filler object to keep page iterable if it was iterable. |
+ int remaining = |
+ static_cast<int>(allocation_info_.limit - allocation_info_.top); |
+ heap()->CreateFillerObjectAt(allocation_info_.top, remaining); |
-bool PagedSpace::ReserveSpace(int bytes) { |
- Address limit = allocation_info_.limit; |
- Address top = allocation_info_.top; |
- if (limit - top >= bytes) return true; |
- |
- // There wasn't enough space in the current page. Lets put the rest |
- // of the page on the free list and start a fresh page. |
- PutRestOfCurrentPageOnFreeList(TopPageOf(allocation_info_)); |
- |
- Page* reserved_page = TopPageOf(allocation_info_); |
- int bytes_left_to_reserve = bytes; |
- while (bytes_left_to_reserve > 0) { |
- if (!reserved_page->next_page()->is_valid()) { |
- if (heap()->OldGenerationAllocationLimitReached()) return false; |
- Expand(reserved_page); |
- } |
- bytes_left_to_reserve -= Page::kPageSize; |
- reserved_page = reserved_page->next_page(); |
- if (!reserved_page->is_valid()) return false; |
+ allocation_info_.top = NULL; |
+ allocation_info_.limit = NULL; |
} |
- ASSERT(TopPageOf(allocation_info_)->next_page()->is_valid()); |
- TopPageOf(allocation_info_)->next_page()->InvalidateWatermark(true); |
- SetAllocationInfo(&allocation_info_, |
- TopPageOf(allocation_info_)->next_page()); |
- return true; |
} |
-// You have to call this last, since the implementation from PagedSpace |
-// doesn't know that memory was 'promised' to large object space. |
-bool LargeObjectSpace::ReserveSpace(int bytes) { |
- return heap()->OldGenerationSpaceAvailable() >= bytes; |
-} |
+HeapObject* PagedSpace::SlowAllocateRaw(int size_in_bytes) { |
+ // Allocation in this space has failed. |
- |
-// Slow case for normal allocation. Try in order: (1) allocate in the next |
-// page in the space, (2) allocate off the space's free list, (3) expand the |
-// space, (4) fail. |
-HeapObject* OldSpace::SlowAllocateRaw(int size_in_bytes) { |
- // Linear allocation in this space has failed. If there is another page |
- // in the space, move to that page and allocate there. This allocation |
- // should succeed (size_in_bytes should not be greater than a page's |
- // object area size). |
- Page* current_page = TopPageOf(allocation_info_); |
- if (current_page->next_page()->is_valid()) { |
- return AllocateInNextPage(current_page, size_in_bytes); |
- } |
- |
- // There is no next page in this space. Try free list allocation unless that |
- // is currently forbidden. |
- if (!heap()->linear_allocation()) { |
- int wasted_bytes; |
- Object* result; |
- MaybeObject* maybe = free_list_.Allocate(size_in_bytes, &wasted_bytes); |
- accounting_stats_.WasteBytes(wasted_bytes); |
- if (maybe->ToObject(&result)) { |
- accounting_stats_.AllocateBytes(size_in_bytes); |
- |
- HeapObject* obj = HeapObject::cast(result); |
- Page* p = Page::FromAddress(obj->address()); |
- |
- if (obj->address() >= p->AllocationWatermark()) { |
- // There should be no hole between the allocation watermark |
- // and allocated object address. |
- // Memory above the allocation watermark was not swept and |
- // might contain garbage pointers to new space. |
- ASSERT(obj->address() == p->AllocationWatermark()); |
- p->SetAllocationWatermark(obj->address() + size_in_bytes); |
- } |
- |
- return obj; |
- } |
- } |
- |
// Free list allocation failed and there is no next page. Fail if we have |
// hit the old generation size limit that should cause a garbage |
// collection. |
@@ -2253,64 +2041,33 @@ |
return NULL; |
} |
- // Try to expand the space and allocate in the new next page. |
- ASSERT(!current_page->next_page()->is_valid()); |
- if (Expand(current_page)) { |
- return AllocateInNextPage(current_page, size_in_bytes); |
- } |
+ // If there are unswept pages advance lazy sweeper. |
+ if (first_unswept_page_->is_valid()) { |
+ AdvanceSweeper(size_in_bytes); |
- // Finally, fail. |
- return NULL; |
-} |
+ // Retry the free list allocation. |
+ HeapObject* object = free_list_.Allocate(size_in_bytes); |
+ if (object != NULL) return object; |
+ if (!IsSweepingComplete()) { |
+ AdvanceSweeper(kMaxInt); |
-void OldSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) { |
- current_page->SetAllocationWatermark(allocation_info_.top); |
- int free_size = |
- static_cast<int>(current_page->ObjectAreaEnd() - allocation_info_.top); |
- if (free_size > 0) { |
- int wasted_bytes = free_list_.Free(allocation_info_.top, free_size); |
- accounting_stats_.WasteBytes(wasted_bytes); |
+ // Retry the free list allocation. |
+ object = free_list_.Allocate(size_in_bytes); |
+ if (object != NULL) return object; |
+ } |
} |
-} |
- |
-void FixedSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) { |
- current_page->SetAllocationWatermark(allocation_info_.top); |
- int free_size = |
- static_cast<int>(current_page->ObjectAreaEnd() - allocation_info_.top); |
- // In the fixed space free list all the free list items have the right size. |
- // We use up the rest of the page while preserving this invariant. |
- while (free_size >= object_size_in_bytes_) { |
- free_list_.Free(allocation_info_.top); |
- allocation_info_.top += object_size_in_bytes_; |
- free_size -= object_size_in_bytes_; |
- accounting_stats_.WasteBytes(object_size_in_bytes_); |
+ // Try to expand the space and allocate in the new next page. |
+ if (Expand()) { |
+ return free_list_.Allocate(size_in_bytes); |
} |
-} |
- |
-// Add the block at the top of the page to the space's free list, set the |
-// allocation info to the next page (assumed to be one), and allocate |
-// linearly there. |
-HeapObject* OldSpace::AllocateInNextPage(Page* current_page, |
- int size_in_bytes) { |
- ASSERT(current_page->next_page()->is_valid()); |
- Page* next_page = current_page->next_page(); |
- next_page->ClearGCFields(); |
- PutRestOfCurrentPageOnFreeList(current_page); |
- SetAllocationInfo(&allocation_info_, next_page); |
- return AllocateLinearly(&allocation_info_, size_in_bytes); |
+ // Finally, fail. |
+ return NULL; |
} |
-void OldSpace::DeallocateBlock(Address start, |
- int size_in_bytes, |
- bool add_to_freelist) { |
- Free(start, size_in_bytes, add_to_freelist); |
-} |
- |
- |
#ifdef DEBUG |
void PagedSpace::ReportCodeStatistics() { |
Isolate* isolate = Isolate::Current(); |
@@ -2413,7 +2170,7 @@ |
void PagedSpace::CollectCodeStatistics() { |
Isolate* isolate = heap()->isolate(); |
HeapObjectIterator obj_it(this); |
- for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) { |
+ for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) { |
if (obj->IsCode()) { |
Code* code = Code::cast(obj); |
isolate->code_kind_statistics()[code->kind()] += code->Size(); |
@@ -2438,16 +2195,17 @@ |
} |
-void OldSpace::ReportStatistics() { |
+void PagedSpace::ReportStatistics() { |
int pct = static_cast<int>(Available() * 100 / Capacity()); |
PrintF(" capacity: %" V8_PTR_PREFIX "d" |
", waste: %" V8_PTR_PREFIX "d" |
", available: %" V8_PTR_PREFIX "d, %%%d\n", |
Capacity(), Waste(), Available(), pct); |
+ if (was_swept_conservatively_) return; |
ClearHistograms(); |
HeapObjectIterator obj_it(this); |
- for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) |
+ for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) |
CollectHistogramInfo(obj); |
ReportHistogram(true); |
} |
@@ -2456,192 +2214,28 @@ |
// ----------------------------------------------------------------------------- |
// FixedSpace implementation |
-void FixedSpace::PrepareForMarkCompact(bool will_compact) { |
+void FixedSpace::PrepareForMarkCompact() { |
// Call prepare of the super class. |
- PagedSpace::PrepareForMarkCompact(will_compact); |
+ PagedSpace::PrepareForMarkCompact(); |
- if (will_compact) { |
- // Reset relocation info. |
- MCResetRelocationInfo(); |
+ // During a non-compacting collection, everything below the linear |
+ // allocation pointer except wasted top-of-page blocks is considered |
+ // allocated and we will rediscover available bytes during the |
+ // collection. |
+ accounting_stats_.AllocateBytes(free_list_.available()); |
- // During a compacting collection, everything in the space is considered |
- // 'available' (set by the call to MCResetRelocationInfo) and we will |
- // rediscover live and wasted bytes during the collection. |
- ASSERT(Available() == Capacity()); |
- } else { |
- // During a non-compacting collection, everything below the linear |
- // allocation pointer except wasted top-of-page blocks is considered |
- // allocated and we will rediscover available bytes during the |
- // collection. |
- accounting_stats_.AllocateBytes(free_list_.available()); |
- } |
- |
// Clear the free list before a full GC---it will be rebuilt afterward. |
free_list_.Reset(); |
} |
-void FixedSpace::MCCommitRelocationInfo() { |
- // Update fast allocation info. |
- allocation_info_.top = mc_forwarding_info_.top; |
- allocation_info_.limit = mc_forwarding_info_.limit; |
- ASSERT(allocation_info_.VerifyPagedAllocation()); |
- |
- // The space is compacted and we haven't yet wasted any space. |
- ASSERT(Waste() == 0); |
- |
- // Update allocation_top of each page in use and compute waste. |
- int computed_size = 0; |
- PageIterator it(this, PageIterator::PAGES_USED_BY_MC); |
- while (it.has_next()) { |
- Page* page = it.next(); |
- Address page_top = page->AllocationTop(); |
- computed_size += static_cast<int>(page_top - page->ObjectAreaStart()); |
- if (it.has_next()) { |
- accounting_stats_.WasteBytes( |
- static_cast<int>(page->ObjectAreaEnd() - page_top)); |
- page->SetAllocationWatermark(page_top); |
- } |
- } |
- |
- // Make sure the computed size - based on the used portion of the |
- // pages in use - matches the size we adjust during allocation. |
- ASSERT(computed_size == Size()); |
-} |
- |
- |
-// Slow case for normal allocation. Try in order: (1) allocate in the next |
-// page in the space, (2) allocate off the space's free list, (3) expand the |
-// space, (4) fail. |
-HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) { |
- ASSERT_EQ(object_size_in_bytes_, size_in_bytes); |
- // Linear allocation in this space has failed. If there is another page |
- // in the space, move to that page and allocate there. This allocation |
- // should succeed. |
- Page* current_page = TopPageOf(allocation_info_); |
- if (current_page->next_page()->is_valid()) { |
- return AllocateInNextPage(current_page, size_in_bytes); |
- } |
- |
- // There is no next page in this space. Try free list allocation unless |
- // that is currently forbidden. The fixed space free list implicitly assumes |
- // that all free blocks are of the fixed size. |
- if (!heap()->linear_allocation()) { |
- Object* result; |
- MaybeObject* maybe = free_list_.Allocate(); |
- if (maybe->ToObject(&result)) { |
- accounting_stats_.AllocateBytes(size_in_bytes); |
- HeapObject* obj = HeapObject::cast(result); |
- Page* p = Page::FromAddress(obj->address()); |
- |
- if (obj->address() >= p->AllocationWatermark()) { |
- // There should be no hole between the allocation watermark |
- // and allocated object address. |
- // Memory above the allocation watermark was not swept and |
- // might contain garbage pointers to new space. |
- ASSERT(obj->address() == p->AllocationWatermark()); |
- p->SetAllocationWatermark(obj->address() + size_in_bytes); |
- } |
- |
- return obj; |
- } |
- } |
- |
- // Free list allocation failed and there is no next page. Fail if we have |
- // hit the old generation size limit that should cause a garbage |
- // collection. |
- if (!heap()->always_allocate() && |
- heap()->OldGenerationAllocationLimitReached()) { |
- return NULL; |
- } |
- |
- // Try to expand the space and allocate in the new next page. |
- ASSERT(!current_page->next_page()->is_valid()); |
- if (Expand(current_page)) { |
- return AllocateInNextPage(current_page, size_in_bytes); |
- } |
- |
- // Finally, fail. |
- return NULL; |
-} |
- |
- |
-// Move to the next page (there is assumed to be one) and allocate there. |
-// The top of page block is always wasted, because it is too small to hold a |
-// map. |
-HeapObject* FixedSpace::AllocateInNextPage(Page* current_page, |
- int size_in_bytes) { |
- ASSERT(current_page->next_page()->is_valid()); |
- ASSERT(allocation_info_.top == PageAllocationLimit(current_page)); |
- ASSERT_EQ(object_size_in_bytes_, size_in_bytes); |
- Page* next_page = current_page->next_page(); |
- next_page->ClearGCFields(); |
- current_page->SetAllocationWatermark(allocation_info_.top); |
- accounting_stats_.WasteBytes(page_extra_); |
- SetAllocationInfo(&allocation_info_, next_page); |
- return AllocateLinearly(&allocation_info_, size_in_bytes); |
-} |
- |
- |
-void FixedSpace::DeallocateBlock(Address start, |
- int size_in_bytes, |
- bool add_to_freelist) { |
- // Free-list elements in fixed space are assumed to have a fixed size. |
- // We break the free block into chunks and add them to the free list |
- // individually. |
- int size = object_size_in_bytes(); |
- ASSERT(size_in_bytes % size == 0); |
- Address end = start + size_in_bytes; |
- for (Address a = start; a < end; a += size) { |
- Free(a, add_to_freelist); |
- } |
-} |
- |
- |
-#ifdef DEBUG |
-void FixedSpace::ReportStatistics() { |
- int pct = static_cast<int>(Available() * 100 / Capacity()); |
- PrintF(" capacity: %" V8_PTR_PREFIX "d" |
- ", waste: %" V8_PTR_PREFIX "d" |
- ", available: %" V8_PTR_PREFIX "d, %%%d\n", |
- Capacity(), Waste(), Available(), pct); |
- |
- ClearHistograms(); |
- HeapObjectIterator obj_it(this); |
- for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) |
- CollectHistogramInfo(obj); |
- ReportHistogram(false); |
-} |
-#endif |
- |
- |
// ----------------------------------------------------------------------------- |
// MapSpace implementation |
-void MapSpace::PrepareForMarkCompact(bool will_compact) { |
- // Call prepare of the super class. |
- FixedSpace::PrepareForMarkCompact(will_compact); |
- |
- if (will_compact) { |
- // Initialize map index entry. |
- int page_count = 0; |
- PageIterator it(this, PageIterator::ALL_PAGES); |
- while (it.has_next()) { |
- ASSERT_MAP_PAGE_INDEX(page_count); |
- |
- Page* p = it.next(); |
- ASSERT(p->mc_page_index == page_count); |
- |
- page_addresses_[page_count++] = p->address(); |
- } |
- } |
-} |
- |
- |
#ifdef DEBUG |
void MapSpace::VerifyObject(HeapObject* object) { |
// The object should be a map or a free-list node. |
- ASSERT(object->IsMap() || object->IsByteArray()); |
+ ASSERT(object->IsMap() || object->IsFreeSpace()); |
} |
#endif |
@@ -2662,107 +2256,40 @@ |
// LargeObjectIterator |
LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) { |
- current_ = space->first_chunk_; |
+ current_ = space->first_page_; |
size_func_ = NULL; |
} |
LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space, |
HeapObjectCallback size_func) { |
- current_ = space->first_chunk_; |
+ current_ = space->first_page_; |
size_func_ = size_func; |
} |
-HeapObject* LargeObjectIterator::next() { |
+HeapObject* LargeObjectIterator::Next() { |
if (current_ == NULL) return NULL; |
HeapObject* object = current_->GetObject(); |
- current_ = current_->next(); |
+ current_ = current_->next_page(); |
return object; |
} |
// ----------------------------------------------------------------------------- |
-// LargeObjectChunk |
- |
-LargeObjectChunk* LargeObjectChunk::New(int size_in_bytes, |
- Executability executable) { |
- size_t requested = ChunkSizeFor(size_in_bytes); |
- size_t size; |
- size_t guard_size = (executable == EXECUTABLE) ? Page::kPageSize : 0; |
- Isolate* isolate = Isolate::Current(); |
- void* mem = isolate->memory_allocator()->AllocateRawMemory( |
- requested + guard_size, &size, executable); |
- if (mem == NULL) return NULL; |
- |
- // The start of the chunk may be overlayed with a page so we have to |
- // make sure that the page flags fit in the size field. |
- ASSERT((size & Page::kPageFlagMask) == 0); |
- |
- LOG(isolate, NewEvent("LargeObjectChunk", mem, size)); |
- if (size < requested + guard_size) { |
- isolate->memory_allocator()->FreeRawMemory( |
- mem, size, executable); |
- LOG(isolate, DeleteEvent("LargeObjectChunk", mem)); |
- return NULL; |
- } |
- |
- if (guard_size != 0) { |
- OS::Guard(mem, guard_size); |
- size -= guard_size; |
- mem = static_cast<Address>(mem) + guard_size; |
- } |
- |
- ObjectSpace space = (executable == EXECUTABLE) |
- ? kObjectSpaceCodeSpace |
- : kObjectSpaceLoSpace; |
- isolate->memory_allocator()->PerformAllocationCallback( |
- space, kAllocationActionAllocate, size); |
- |
- LargeObjectChunk* chunk = reinterpret_cast<LargeObjectChunk*>(mem); |
- chunk->size_ = size; |
- chunk->GetPage()->heap_ = isolate->heap(); |
- return chunk; |
-} |
- |
- |
-void LargeObjectChunk::Free(Executability executable) { |
- size_t guard_size = (executable == EXECUTABLE) ? Page::kPageSize : 0; |
- ObjectSpace space = |
- (executable == EXECUTABLE) ? kObjectSpaceCodeSpace : kObjectSpaceLoSpace; |
- // Do not access instance fields after FreeRawMemory! |
- Address my_address = address(); |
- size_t my_size = size(); |
- Isolate* isolate = GetPage()->heap_->isolate(); |
- MemoryAllocator* a = isolate->memory_allocator(); |
- a->FreeRawMemory(my_address - guard_size, my_size + guard_size, executable); |
- a->PerformAllocationCallback(space, kAllocationActionFree, my_size); |
- LOG(isolate, DeleteEvent("LargeObjectChunk", my_address)); |
-} |
- |
- |
-int LargeObjectChunk::ChunkSizeFor(int size_in_bytes) { |
- int os_alignment = static_cast<int>(OS::AllocateAlignment()); |
- if (os_alignment < Page::kPageSize) { |
- size_in_bytes += (Page::kPageSize - os_alignment); |
- } |
- return size_in_bytes + Page::kObjectStartOffset; |
-} |
- |
-// ----------------------------------------------------------------------------- |
// LargeObjectSpace |
LargeObjectSpace::LargeObjectSpace(Heap* heap, AllocationSpace id) |
: Space(heap, id, NOT_EXECUTABLE), // Managed on a per-allocation basis |
- first_chunk_(NULL), |
+ first_page_(NULL), |
size_(0), |
page_count_(0), |
objects_size_(0) {} |
bool LargeObjectSpace::Setup() { |
- first_chunk_ = NULL; |
+ first_page_ = NULL; |
size_ = 0; |
page_count_ = 0; |
objects_size_ = 0; |
@@ -2771,20 +2298,22 @@ |
void LargeObjectSpace::TearDown() { |
- while (first_chunk_ != NULL) { |
- LargeObjectChunk* chunk = first_chunk_; |
- first_chunk_ = first_chunk_->next(); |
- chunk->Free(chunk->GetPage()->PageExecutability()); |
+ while (first_page_ != NULL) { |
+ LargePage* page = first_page_; |
+ first_page_ = first_page_->next_page(); |
+ LOG(heap()->isolate(), DeleteEvent("LargeObjectChunk", page->address())); |
+ |
+ ObjectSpace space = static_cast<ObjectSpace>(1 << identity()); |
+ heap()->isolate()->memory_allocator()->PerformAllocationCallback( |
+ space, kAllocationActionFree, page->size()); |
+ heap()->isolate()->memory_allocator()->Free(page); |
} |
Setup(); |
} |
-MaybeObject* LargeObjectSpace::AllocateRawInternal(int requested_size, |
- int object_size, |
- Executability executable) { |
- ASSERT(0 < object_size && object_size <= requested_size); |
- |
+MaybeObject* LargeObjectSpace::AllocateRaw(int object_size, |
+ Executability executable) { |
// Check if we want to force a GC before growing the old space further. |
// If so, fail the allocation. |
if (!heap()->always_allocate() && |
@@ -2792,75 +2321,42 @@ |
return Failure::RetryAfterGC(identity()); |
} |
- LargeObjectChunk* chunk = LargeObjectChunk::New(requested_size, executable); |
- if (chunk == NULL) { |
- return Failure::RetryAfterGC(identity()); |
- } |
+ LargePage* page = heap()->isolate()->memory_allocator()-> |
+ AllocateLargePage(object_size, executable, this); |
+ if (page == NULL) return Failure::RetryAfterGC(identity()); |
+ ASSERT(page->body_size() >= object_size); |
- size_ += static_cast<int>(chunk->size()); |
- objects_size_ += requested_size; |
+ size_ += static_cast<int>(page->size()); |
+ objects_size_ += object_size; |
page_count_++; |
- chunk->set_next(first_chunk_); |
- first_chunk_ = chunk; |
+ page->set_next_page(first_page_); |
+ first_page_ = page; |
- // Initialize page header. |
- Page* page = chunk->GetPage(); |
- Address object_address = page->ObjectAreaStart(); |
- |
- // Clear the low order bit of the second word in the page to flag it as a |
- // large object page. If the chunk_size happened to be written there, its |
- // low order bit should already be clear. |
- page->SetIsLargeObjectPage(true); |
- page->SetPageExecutability(executable); |
- page->SetRegionMarks(Page::kAllRegionsCleanMarks); |
- return HeapObject::FromAddress(object_address); |
+ heap()->incremental_marking()->OldSpaceStep(object_size); |
+ return page->GetObject(); |
} |
-MaybeObject* LargeObjectSpace::AllocateRawCode(int size_in_bytes) { |
- ASSERT(0 < size_in_bytes); |
- return AllocateRawInternal(size_in_bytes, |
- size_in_bytes, |
- EXECUTABLE); |
-} |
- |
- |
-MaybeObject* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) { |
- ASSERT(0 < size_in_bytes); |
- return AllocateRawInternal(size_in_bytes, |
- size_in_bytes, |
- NOT_EXECUTABLE); |
-} |
- |
- |
-MaybeObject* LargeObjectSpace::AllocateRaw(int size_in_bytes) { |
- ASSERT(0 < size_in_bytes); |
- return AllocateRawInternal(size_in_bytes, |
- size_in_bytes, |
- NOT_EXECUTABLE); |
-} |
- |
- |
// GC support |
MaybeObject* LargeObjectSpace::FindObject(Address a) { |
- for (LargeObjectChunk* chunk = first_chunk_; |
- chunk != NULL; |
- chunk = chunk->next()) { |
- Address chunk_address = chunk->address(); |
- if (chunk_address <= a && a < chunk_address + chunk->size()) { |
- return chunk->GetObject(); |
+ for (LargePage* page = first_page_; |
+ page != NULL; |
+ page = page->next_page()) { |
+ Address page_address = page->address(); |
+ if (page_address <= a && a < page_address + page->size()) { |
+ return page->GetObject(); |
} |
} |
return Failure::Exception(); |
} |
-LargeObjectChunk* LargeObjectSpace::FindChunkContainingPc(Address pc) { |
+LargePage* LargeObjectSpace::FindPageContainingPc(Address pc) { |
// TODO(853): Change this implementation to only find executable |
// chunks and use some kind of hash-based approach to speed it up. |
- for (LargeObjectChunk* chunk = first_chunk_; |
+ for (LargePage* chunk = first_page_; |
chunk != NULL; |
- chunk = chunk->next()) { |
+ chunk = chunk->next_page()) { |
Address chunk_address = chunk->address(); |
if (chunk_address <= pc && pc < chunk_address + chunk->size()) { |
return chunk; |
@@ -2870,112 +2366,57 @@ |
} |
-void LargeObjectSpace::IterateDirtyRegions(ObjectSlotCallback copy_object) { |
- LargeObjectIterator it(this); |
- for (HeapObject* object = it.next(); object != NULL; object = it.next()) { |
- // We only have code, sequential strings, or fixed arrays in large |
- // object space, and only fixed arrays can possibly contain pointers to |
- // the young generation. |
- if (object->IsFixedArray()) { |
- Page* page = Page::FromAddress(object->address()); |
- uint32_t marks = page->GetRegionMarks(); |
- uint32_t newmarks = Page::kAllRegionsCleanMarks; |
- |
- if (marks != Page::kAllRegionsCleanMarks) { |
- // For a large page a single dirty mark corresponds to several |
- // regions (modulo 32). So we treat a large page as a sequence of |
- // normal pages of size Page::kPageSize having same dirty marks |
- // and subsequently iterate dirty regions on each of these pages. |
- Address start = object->address(); |
- Address end = page->ObjectAreaEnd(); |
- Address object_end = start + object->Size(); |
- |
- // Iterate regions of the first normal page covering object. |
- uint32_t first_region_number = page->GetRegionNumberForAddress(start); |
- newmarks |= |
- heap()->IterateDirtyRegions(marks >> first_region_number, |
- start, |
- end, |
- &Heap::IteratePointersInDirtyRegion, |
- copy_object) << first_region_number; |
- |
- start = end; |
- end = start + Page::kPageSize; |
- while (end <= object_end) { |
- // Iterate next 32 regions. |
- newmarks |= |
- heap()->IterateDirtyRegions(marks, |
- start, |
- end, |
- &Heap::IteratePointersInDirtyRegion, |
- copy_object); |
- start = end; |
- end = start + Page::kPageSize; |
- } |
- |
- if (start != object_end) { |
- // Iterate the last piece of an object which is less than |
- // Page::kPageSize. |
- newmarks |= |
- heap()->IterateDirtyRegions(marks, |
- start, |
- object_end, |
- &Heap::IteratePointersInDirtyRegion, |
- copy_object); |
- } |
- |
- page->SetRegionMarks(newmarks); |
- } |
- } |
- } |
-} |
- |
- |
void LargeObjectSpace::FreeUnmarkedObjects() { |
- LargeObjectChunk* previous = NULL; |
- LargeObjectChunk* current = first_chunk_; |
+ LargePage* previous = NULL; |
+ LargePage* current = first_page_; |
while (current != NULL) { |
HeapObject* object = current->GetObject(); |
- if (object->IsMarked()) { |
- object->ClearMark(); |
- heap()->mark_compact_collector()->tracer()->decrement_marked_count(); |
+ // Can this large page contain pointers to non-trivial objects. No other |
+ // pointer object is this big. |
+ bool is_pointer_object = object->IsFixedArray(); |
+ MarkBit mark_bit = Marking::MarkBitFrom(object); |
+ if (mark_bit.Get()) { |
+ mark_bit.Clear(); |
+ MemoryChunk::IncrementLiveBytes(object->address(), -object->Size()); |
previous = current; |
- current = current->next(); |
+ current = current->next_page(); |
} else { |
+ LargePage* page = current; |
// Cut the chunk out from the chunk list. |
- LargeObjectChunk* current_chunk = current; |
- current = current->next(); |
+ current = current->next_page(); |
if (previous == NULL) { |
- first_chunk_ = current; |
+ first_page_ = current; |
} else { |
- previous->set_next(current); |
+ previous->set_next_page(current); |
} |
// Free the chunk. |
heap()->mark_compact_collector()->ReportDeleteIfNeeded( |
object, heap()->isolate()); |
- LiveObjectList::ProcessNonLive(object); |
- |
- size_ -= static_cast<int>(current_chunk->size()); |
+ size_ -= static_cast<int>(page->size()); |
objects_size_ -= object->Size(); |
page_count_--; |
- current_chunk->Free(current_chunk->GetPage()->PageExecutability()); |
+ |
+ if (is_pointer_object) { |
+ heap()->QueueMemoryChunkForFree(page); |
+ } else { |
+ heap()->isolate()->memory_allocator()->Free(page); |
+ } |
} |
} |
+ heap()->FreeQueuedChunks(); |
} |
bool LargeObjectSpace::Contains(HeapObject* object) { |
Address address = object->address(); |
- if (heap()->new_space()->Contains(address)) { |
- return false; |
- } |
- Page* page = Page::FromAddress(address); |
+ MemoryChunk* chunk = MemoryChunk::FromAddress(address); |
- SLOW_ASSERT(!page->IsLargeObjectPage() |
- || !FindObject(address)->IsFailure()); |
+ bool owned = (chunk->owner() == this); |
- return page->IsLargeObjectPage(); |
+ SLOW_ASSERT(!owned || !FindObject(address)->IsFailure()); |
+ |
+ return owned; |
} |
@@ -2983,9 +2424,9 @@ |
// We do not assume that the large object iterator works, because it depends |
// on the invariants we are checking during verification. |
void LargeObjectSpace::Verify() { |
- for (LargeObjectChunk* chunk = first_chunk_; |
+ for (LargePage* chunk = first_page_; |
chunk != NULL; |
- chunk = chunk->next()) { |
+ chunk = chunk->next_page()) { |
// Each chunk contains an object that starts at the large object page's |
// object area start. |
HeapObject* object = chunk->GetObject(); |
@@ -3015,9 +2456,6 @@ |
object->Size(), |
&code_visitor); |
} else if (object->IsFixedArray()) { |
- // We loop over fixed arrays ourselves, rather then using the visitor, |
- // because the visitor doesn't support the start/offset iteration |
- // needed for IsRegionDirty. |
FixedArray* array = FixedArray::cast(object); |
for (int j = 0; j < array->length(); j++) { |
Object* element = array->get(j); |
@@ -3025,13 +2463,6 @@ |
HeapObject* element_object = HeapObject::cast(element); |
ASSERT(heap()->Contains(element_object)); |
ASSERT(element_object->map()->IsMap()); |
- if (heap()->InNewSpace(element_object)) { |
- Address array_addr = object->address(); |
- Address element_addr = array_addr + FixedArray::kHeaderSize + |
- j * kPointerSize; |
- |
- ASSERT(Page::FromAddress(array_addr)->IsRegionDirty(element_addr)); |
- } |
} |
} |
} |
@@ -3041,7 +2472,7 @@ |
void LargeObjectSpace::Print() { |
LargeObjectIterator it(this); |
- for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) { |
+ for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { |
obj->Print(); |
} |
} |
@@ -3052,7 +2483,7 @@ |
int num_objects = 0; |
ClearHistograms(); |
LargeObjectIterator it(this); |
- for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) { |
+ for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { |
num_objects++; |
CollectHistogramInfo(obj); |
} |
@@ -3066,7 +2497,7 @@ |
void LargeObjectSpace::CollectCodeStatistics() { |
Isolate* isolate = heap()->isolate(); |
LargeObjectIterator obj_it(this); |
- for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) { |
+ for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) { |
if (obj->IsCode()) { |
Code* code = Code::cast(obj); |
isolate->code_kind_statistics()[code->kind()] += code->Size(); |