Chromium Code Reviews| Index: src/spaces.cc |
| diff --git a/src/spaces.cc b/src/spaces.cc |
| index 46f868f70ff7470db3ab5ba3125c31cee2e05dae..082289a5e27c2a9eae147ecfb960402d0f855baa 100644 |
| --- a/src/spaces.cc |
| +++ b/src/spaces.cc |
| @@ -126,9 +126,6 @@ PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) { |
| 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 |
| @@ -233,13 +230,15 @@ void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { |
| GetNextAllocationBlock(requested); |
| } |
| // Commit the requested memory at the start of the current allocation block. |
| - *allocated = RoundUp(requested, Page::kPageSize); |
| + *allocated = RoundUp(requested, MemoryChunk::kAlignment); |
| FreeBlock current = allocation_list_[current_allocation_block_index_]; |
| if (*allocated >= current.size - Page::kPageSize) { |
| // Don't leave a small free block, useless for a large object or chunk. |
| *allocated = current.size; |
| } |
| ASSERT(*allocated <= current.size); |
| + ASSERT(IsAligned(static_cast<intptr_t>(*allocated), MemoryChunk::kAlignment)); |
| + ASSERT(IsAligned(OffsetFrom(current.start), MemoryChunk::kAlignment)); |
| if (!code_range_->Commit(current.start, *allocated, true)) { |
| *allocated = 0; |
| return NULL; |
| @@ -254,6 +253,9 @@ void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { |
| void CodeRange::FreeRawMemory(void* address, size_t length) { |
| + ASSERT(IsAligned(reinterpret_cast<intptr_t>(address), |
| + MemoryChunk::kAlignment)); |
| + ASSERT(IsAligned(static_cast<intptr_t>(length), MemoryChunk::kAlignment)); |
| free_list_.Add(FreeBlock(address, length)); |
| code_range_->Uncommit(address, length); |
| } |
| @@ -270,316 +272,222 @@ void CodeRange::TearDown() { |
| // ----------------------------------------------------------------------------- |
| // MemoryAllocator |
| // |
| -intptr_t MemoryAllocator::capacity_ = 0; |
| -intptr_t MemoryAllocator::capacity_executable_ = 0; |
| -intptr_t MemoryAllocator::size_ = 0; |
| -intptr_t MemoryAllocator::size_executable_ = 0; |
| +size_t MemoryAllocator::capacity_ = 0; |
| +size_t MemoryAllocator::capacity_executable_ = 0; |
| +size_t MemoryAllocator::size_ = 0; |
| +size_t MemoryAllocator::size_executable_ = 0; |
| List<MemoryAllocator::MemoryAllocationCallbackRegistration> |
| MemoryAllocator::memory_allocation_callbacks_; |
| -VirtualMemory* MemoryAllocator::initial_chunk_ = NULL; |
| - |
| -// 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; |
| -List<MemoryAllocator::ChunkInfo> MemoryAllocator::chunks_( |
| - kEstimatedNumberOfChunks); |
| -List<int> MemoryAllocator::free_chunk_ids_(kEstimatedNumberOfChunks); |
| -int MemoryAllocator::max_nof_chunks_ = 0; |
| -int MemoryAllocator::top_ = 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; |
| } |
| -bool MemoryAllocator::SafeIsInAPageChunk(Address addr) { |
| - return InInitialChunk(addr) || InAllocatedChunks(addr); |
| +void MemoryAllocator::TearDown() { |
| + capacity_ = 0; |
| + capacity_executable_ = 0; |
| + size_ = 0; |
| + size_executable_ = 0; |
| } |
| -void MemoryAllocator::TearDown() { |
| - for (int i = 0; i < max_nof_chunks_; i++) { |
| - if (chunks_[i].address() != NULL) DeleteChunk(i); |
| +void MemoryAllocator::FreeMemory(void* base, |
| + size_t size, |
| + Executability executable) { |
| + if (CodeRange::contains(static_cast<Address>(base))) { |
| + ASSERT(executable == EXECUTABLE); |
| + CodeRange::FreeRawMemory(base, size); |
| + } else { |
| + ASSERT(executable == NOT_EXECUTABLE || !CodeRange::exists()); |
| + VirtualMemory::ReleaseRegion(base, size); |
| } |
| - chunks_.Clear(); |
| - free_chunk_ids_.Clear(); |
| - if (initial_chunk_ != NULL) { |
| - LOG(DeleteEvent("InitialChunk", initial_chunk_->address())); |
| - delete initial_chunk_; |
| - initial_chunk_ = NULL; |
| - } |
| + Counters::memory_allocated.Decrement(static_cast<int>(size)); |
| - FreeChunkTables(&chunk_table_[0], |
| - kChunkTableTopLevelEntries, |
| - kChunkTableLevels); |
| + ASSERT(size_ >= size); |
| + size_ -= size; |
| - ASSERT(top_ == max_nof_chunks_); // all chunks are free |
| - top_ = 0; |
| - capacity_ = 0; |
| - capacity_executable_ = 0; |
| - size_ = 0; |
| - max_nof_chunks_ = 0; |
| + if (executable == EXECUTABLE) { |
| + ASSERT(size_executable_ >= size); |
| + size_executable_ -= size; |
| + } |
| } |
| -void MemoryAllocator::FreeChunkTables(uintptr_t* array, int len, int level) { |
| - for (int i = 0; i < len; i++) { |
| - if (array[i] != kUnusedChunkTableEntry) { |
| - uintptr_t* subarray = reinterpret_cast<uintptr_t*>(array[i]); |
| - if (level > 1) { |
| - array[i] = kUnusedChunkTableEntry; |
| - FreeChunkTables(subarray, 1 << kChunkTableBitsPerLevel, level - 1); |
| - } else { |
| - array[i] = kUnusedChunkTableEntry; |
| - } |
| - delete[] subarray; |
| - } |
| +void* MemoryAllocator::ReserveAlignedMemory(const size_t requested, |
|
Erik Corry
2010/12/22 13:48:27
Shouldn't this return an Address?
Vyacheslav Egorov (Chromium)
2010/12/22 19:55:07
Done. I eradicated void* usages.
|
| + size_t alignment, |
| + size_t* allocated_size) { |
| + ASSERT(IsAligned(alignment, OS::AllocateAlignment())); |
| + if (size_ + requested > capacity_) return false; |
| + |
| + size_t allocated = RoundUp(requested + alignment, OS::AllocateAlignment()); |
| + |
| + uintptr_t base = reinterpret_cast<intptr_t>( |
| + VirtualMemory::ReserveRegion(allocated)); |
| + |
| + uintptr_t end = base + allocated; |
| + |
| + if (base == 0) return NULL; |
| + |
| + uintptr_t aligned_base = RoundUp(base, alignment); |
| + |
| + ASSERT(aligned_base + requested <= base + allocated); |
| + |
| + // The difference between re-aligned base address and base address is |
| + // multiple of OS::AllocateAlignment(). |
| + if (aligned_base != base) { |
| + ASSERT(aligned_base > base); |
| + // TODO(gc) check result of operation? |
| + VirtualMemory::ReleaseRegion(reinterpret_cast<void*>(base), |
| + aligned_base - base); |
| + allocated -= (aligned_base - base); |
| + base = aligned_base; |
| + } |
| + |
| + ASSERT(base + allocated == end); |
| + |
| + uintptr_t requested_end = base + requested; |
| + uintptr_t aligned_requested_end = |
| + RoundUp(requested_end, OS::AllocateAlignment()); |
| + |
| + if (aligned_requested_end < end) { |
| + // TODO(gc) check result of operation? |
| + VirtualMemory::ReleaseRegion(reinterpret_cast<void*>(aligned_requested_end), |
| + end - aligned_requested_end); |
| + allocated = aligned_requested_end - base; |
| } |
| + |
| + size_ += allocated; |
| + *allocated_size = allocated; |
| + return reinterpret_cast<void*>(base); |
| } |
| -void* MemoryAllocator::AllocateRawMemory(const size_t requested, |
| - size_t* allocated, |
| - Executability executable) { |
| - if (size_ + static_cast<size_t>(requested) > static_cast<size_t>(capacity_)) { |
| +void* MemoryAllocator::AllocateAlignedMemory(const size_t requested, |
| + size_t alignment, |
| + Executability executable, |
| + size_t* allocated_size) { |
| + void* base = |
| + ReserveAlignedMemory(requested, Page::kPageSize, allocated_size); |
| + |
| + if (base == NULL) return NULL; |
| + |
| + if (!VirtualMemory::CommitRegion(base, |
| + *allocated_size, |
| + executable == EXECUTABLE)) { |
| + VirtualMemory::ReleaseRegion(base, *allocated_size); |
| + size_ -= *allocated_size; |
| return NULL; |
| } |
| - void* mem; |
| + return base; |
| +} |
| + |
| + |
| +MemoryChunk* MemoryAllocator::AllocateChunk(intptr_t body_size, |
| + Executability executable, |
| + Space* owner) { |
| + size_t chunk_size = MemoryChunk::kBodyOffset + body_size; |
| + void* base = NULL; |
| if (executable == EXECUTABLE) { |
| // Check executable memory limit. |
| - if (size_executable_ + requested > |
| - static_cast<size_t>(capacity_executable_)) { |
| + if (size_executable_ + chunk_size > capacity_executable_) { |
| LOG(StringEvent("MemoryAllocator::AllocateRawMemory", |
| "V8 Executable Allocation capacity exceeded")); |
| return NULL; |
| } |
| + |
| // Allocate executable memory either from code range or from the |
| // OS. |
| if (CodeRange::exists()) { |
| - mem = CodeRange::AllocateRawMemory(requested, allocated); |
| + base = CodeRange::AllocateRawMemory(chunk_size, &chunk_size); |
| + ASSERT(IsAligned(reinterpret_cast<intptr_t>(base), |
| + MemoryChunk::kAlignment)); |
| + size_ += chunk_size; |
| } else { |
| - mem = OS::Allocate(requested, allocated, true); |
| + base = AllocateAlignedMemory(chunk_size, |
| + MemoryChunk::kAlignment, |
| + executable, |
| + &chunk_size); |
| } |
| - // Update executable memory size. |
| - size_executable_ += static_cast<int>(*allocated); |
| - } else { |
| - mem = OS::Allocate(requested, allocated, false); |
| - } |
| - int alloced = static_cast<int>(*allocated); |
| - size_ += alloced; |
| - |
| -#ifdef DEBUG |
| - ZapBlock(reinterpret_cast<Address>(mem), alloced); |
| -#endif |
| - Counters::memory_allocated.Increment(alloced); |
| - return mem; |
| -} |
| + if (base == NULL) return NULL; |
| -void MemoryAllocator::FreeRawMemory(void* mem, |
| - size_t length, |
| - Executability executable) { |
| -#ifdef DEBUG |
| - ZapBlock(reinterpret_cast<Address>(mem), length); |
| -#endif |
| - if (CodeRange::contains(static_cast<Address>(mem))) { |
| - CodeRange::FreeRawMemory(mem, length); |
| + // Update executable memory size. |
| + size_executable_ += chunk_size; |
| } else { |
| - OS::Free(mem, length); |
| - } |
| - 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); |
| -} |
| - |
| + base = AllocateAlignedMemory(chunk_size, |
| + MemoryChunk::kAlignment, |
| + executable, |
| + &chunk_size); |
| -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)); |
| + if (base == NULL) return NULL; |
| } |
| -} |
| +#ifdef DEBUG |
| + ZapBlock(reinterpret_cast<Address>(base), chunk_size); |
| +#endif |
| + Counters::memory_allocated.Increment(chunk_size); |
| -bool MemoryAllocator::MemoryAllocationCallbackRegistered( |
| - MemoryAllocationCallback callback) { |
| - for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { |
| - if (memory_allocation_callbacks_[i].callback == callback) return true; |
| + LOG(NewEvent("MemoryChunk", base, chunk_size)); |
| + if (owner != NULL) { |
| + ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity()); |
| + PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size); |
| } |
| - return false; |
| -} |
| - |
| -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); |
| + return MemoryChunk::Initialize(reinterpret_cast<Address>(base), |
| + chunk_size, |
| + executable, |
| + owner); |
| } |
| -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::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; |
| - } |
| - |
| - // We are sure that we have mapped a block of requested addresses. |
| - ASSERT(initial_chunk_->size() == requested); |
| - LOG(NewEvent("InitialChunk", initial_chunk_->address(), requested)); |
| - size_ += static_cast<int>(requested); |
| - return initial_chunk_->address(); |
| -} |
| +Page* MemoryAllocator::AllocatePage(PagedSpace* owner, |
| + Executability executable) { |
| + MemoryChunk* chunk = AllocateChunk(Page::kObjectAreaSize, executable, owner); |
| + if (chunk == NULL) return NULL; |
| -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); |
| + return Page::Initialize(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* chunk = AllocateRawMemory(chunk_size, &chunk_size, owner->executable()); |
| - if (chunk == NULL) return Page::FromAddress(NULL); |
| - LOG(NewEvent("PagedChunk", chunk, chunk_size)); |
| - |
| - *allocated_pages = PagesInChunk(static_cast<Address>(chunk), chunk_size); |
| - // We may 'lose' a page due to alignment. |
| - ASSERT(*allocated_pages >= kPagesPerChunk - 1); |
| - if (*allocated_pages == 0) { |
| - FreeRawMemory(chunk, chunk_size, owner->executable()); |
| - LOG(DeleteEvent("PagedChunk", chunk)); |
| - return Page::FromAddress(NULL); |
| - } |
| - |
| - int chunk_id = Pop(); |
| - chunks_[chunk_id].init(static_cast<Address>(chunk), chunk_size, owner); |
| - |
| - ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity()); |
| - PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size); |
| - Page* new_pages = InitializePagesInChunk(chunk_id, *allocated_pages, owner); |
| - |
| - AddToAllocatedChunks(static_cast<Address>(chunk), chunk_size); |
| - |
| - return new_pages; |
| +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(chunk); |
| } |
| -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); |
| +void MemoryAllocator::Free(MemoryChunk* chunk) { |
| + LOG(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 |
| - 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); |
| + FreeMemory(reinterpret_cast<void*>(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 |
| @@ -589,13 +497,7 @@ bool MemoryAllocator::CommitBlock(Address start, |
| 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; |
| Counters::memory_allocated.Decrement(static_cast<int>(size)); |
| return true; |
| } |
| @@ -608,128 +510,49 @@ void MemoryAllocator::ZapBlock(Address start, size_t size) { |
| } |
| -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->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 |
| - } |
| - |
| - 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); |
| +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 page_to_return; |
| + return false; |
| } |
| -void MemoryAllocator::FreeAllPages(PagedSpace* space) { |
| - for (int i = 0, length = chunks_.length(); i < length; i++) { |
| - if (chunks_[i].owner() == space) { |
| - DeleteChunk(i); |
| - } |
| - } |
| +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::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::memory_allocated.Decrement(static_cast<int>(c.size())); |
| - } else { |
| - RemoveFromAllocatedChunks(c.address(), c.size()); |
| - LOG(DeleteEvent("PagedChunk", c.address())); |
| - ObjectSpace space = static_cast<ObjectSpace>(1 << c.owner()->identity()); |
| - size_t size = c.size(); |
| - FreeRawMemory(c.address(), size, c.executable()); |
| - PerformAllocationCallback(space, kAllocationActionFree, size); |
| +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; |
| + } |
| } |
| - 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); |
| + UNREACHABLE(); |
| } |
| @@ -743,193 +566,6 @@ void MemoryAllocator::ReportStatistics() { |
| } |
| #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; |
| -} |
| - |
| - |
| -void MemoryAllocator::AddToAllocatedChunks(Address addr, intptr_t size) { |
| - ASSERT(size == kChunkSize); |
| - uintptr_t int_address = reinterpret_cast<uintptr_t>(addr); |
| - AddChunkUsingAddress(int_address, int_address); |
| - AddChunkUsingAddress(int_address, int_address + size - 1); |
| -} |
| - |
| - |
| -void MemoryAllocator::AddChunkUsingAddress(uintptr_t chunk_start, |
| - uintptr_t chunk_index_base) { |
| - uintptr_t* fine_grained = AllocatedChunksFinder( |
| - chunk_table_, |
| - chunk_index_base, |
| - kChunkSizeLog2 + (kChunkTableLevels - 1) * kChunkTableBitsPerLevel, |
| - kCreateTablesAsNeeded); |
| - int index = FineGrainedIndexForAddress(chunk_index_base); |
| - if (fine_grained[index] != kUnusedChunkTableEntry) index++; |
| - ASSERT(fine_grained[index] == kUnusedChunkTableEntry); |
| - fine_grained[index] = chunk_start; |
| -} |
| - |
| - |
| -void MemoryAllocator::RemoveFromAllocatedChunks(Address addr, intptr_t size) { |
| - ASSERT(size == kChunkSize); |
| - uintptr_t int_address = reinterpret_cast<uintptr_t>(addr); |
| - RemoveChunkFoundUsingAddress(int_address, int_address); |
| - RemoveChunkFoundUsingAddress(int_address, int_address + size - 1); |
| -} |
| - |
| - |
| -void MemoryAllocator::RemoveChunkFoundUsingAddress( |
| - uintptr_t chunk_start, |
| - uintptr_t chunk_index_base) { |
| - uintptr_t* fine_grained = AllocatedChunksFinder( |
| - chunk_table_, |
| - chunk_index_base, |
| - kChunkSizeLog2 + (kChunkTableLevels - 1) * kChunkTableBitsPerLevel, |
| - kDontCreateTables); |
| - // Can't remove an entry that's not there. |
| - ASSERT(fine_grained != kUnusedChunkTableEntry); |
| - int index = FineGrainedIndexForAddress(chunk_index_base); |
| - ASSERT(fine_grained[index] != kUnusedChunkTableEntry); |
| - if (fine_grained[index] != chunk_start) { |
| - index++; |
| - ASSERT(fine_grained[index] == chunk_start); |
| - fine_grained[index] = kUnusedChunkTableEntry; |
| - } else { |
| - // If only one of the entries is used it must be the first, since |
| - // InAllocatedChunks relies on that. Move things around so that this is |
| - // the case. |
| - fine_grained[index] = fine_grained[index + 1]; |
| - fine_grained[index + 1] = kUnusedChunkTableEntry; |
| - } |
| -} |
| - |
| - |
| -bool MemoryAllocator::InAllocatedChunks(Address addr) { |
| - uintptr_t int_address = reinterpret_cast<uintptr_t>(addr); |
| - uintptr_t* fine_grained = AllocatedChunksFinder( |
| - chunk_table_, |
| - int_address, |
| - kChunkSizeLog2 + (kChunkTableLevels - 1) * kChunkTableBitsPerLevel, |
| - kDontCreateTables); |
| - if (fine_grained == NULL) return false; |
| - int index = FineGrainedIndexForAddress(int_address); |
| - if (fine_grained[index] == kUnusedChunkTableEntry) return false; |
| - uintptr_t entry = fine_grained[index]; |
| - if (entry <= int_address && entry + kChunkSize > int_address) return true; |
| - index++; |
| - if (fine_grained[index] == kUnusedChunkTableEntry) return false; |
| - entry = fine_grained[index]; |
| - if (entry <= int_address && entry + kChunkSize > int_address) return true; |
| - return false; |
| -} |
| - |
| - |
| -uintptr_t* MemoryAllocator::AllocatedChunksFinder( |
| - uintptr_t* table, |
| - uintptr_t address, |
| - int bit_position, |
| - CreateTables create_as_needed) { |
| - if (bit_position == kChunkSizeLog2) { |
| - return table; |
| - } |
| - ASSERT(bit_position >= kChunkSizeLog2 + kChunkTableBitsPerLevel); |
| - int index = |
| - ((address >> bit_position) & |
| - ((V8_INTPTR_C(1) << kChunkTableBitsPerLevel) - 1)); |
| - uintptr_t more_fine_grained_address = |
| - address & ((V8_INTPTR_C(1) << bit_position) - 1); |
| - ASSERT((table == chunk_table_ && index < kChunkTableTopLevelEntries) || |
| - (table != chunk_table_ && index < 1 << kChunkTableBitsPerLevel)); |
| - uintptr_t* more_fine_grained_table = |
| - reinterpret_cast<uintptr_t*>(table[index]); |
| - if (more_fine_grained_table == kUnusedChunkTableEntry) { |
| - if (create_as_needed == kDontCreateTables) return NULL; |
| - int words_needed = 1 << kChunkTableBitsPerLevel; |
| - if (bit_position == kChunkTableBitsPerLevel + kChunkSizeLog2) { |
| - words_needed = |
| - (1 << kChunkTableBitsPerLevel) * kChunkTableFineGrainedWordsPerEntry; |
| - } |
| - more_fine_grained_table = new uintptr_t[words_needed]; |
| - for (int i = 0; i < words_needed; i++) { |
| - more_fine_grained_table[i] = kUnusedChunkTableEntry; |
| - } |
| - table[index] = reinterpret_cast<uintptr_t>(more_fine_grained_table); |
| - } |
| - return AllocatedChunksFinder( |
| - more_fine_grained_table, |
| - more_fine_grained_address, |
| - bit_position - kChunkTableBitsPerLevel, |
| - create_as_needed); |
| -} |
| - |
| - |
| -uintptr_t MemoryAllocator::chunk_table_[kChunkTableTopLevelEntries]; |
| - |
| - |
| // ----------------------------------------------------------------------------- |
| // PagedSpace implementation |
| @@ -943,51 +579,29 @@ PagedSpace::PagedSpace(intptr_t max_capacity, |
| allocation_info_.top = NULL; |
| allocation_info_.limit = NULL; |
| - |
| - mc_forwarding_info_.top = NULL; |
| - mc_forwarding_info_.limit = NULL; |
| } |
| -bool PagedSpace::Setup(Address start, size_t size) { |
| +bool PagedSpace::Setup() { |
| 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_ = MemoryAllocator::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_ = |
| - MemoryAllocator::AllocatePages(requested_pages, &num_pages, this); |
| - if (!first_page_->is_valid()) return false; |
| - } |
| + // Maximum space capacity can not be less than single page size. |
| + if (max_capacity_ < Page::kPageSize) return false; |
| + |
| + first_page_ = MemoryAllocator::AllocatePage(this, executable()); |
| + 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); |
| + accounting_stats_.ExpandSpace(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; |
| - } |
| + last_page_ = first_page_; |
| + ASSERT(!last_page_->next_page()->is_valid()); |
| // Use first_page_ for allocation. |
| SetAllocationInfo(&allocation_info_, first_page_); |
| - page_list_is_chunk_ordered_ = true; |
| - |
| return true; |
| } |
| @@ -998,8 +612,13 @@ bool PagedSpace::HasBeenSetup() { |
| void PagedSpace::TearDown() { |
| - MemoryAllocator::FreeAllPages(this); |
| + Page* next = NULL; |
| + for (Page* p = first_page_; p->is_valid(); p = next) { |
| + next = p->next_page(); |
| + MemoryAllocator::Free(p); |
| + } |
| first_page_ = NULL; |
| + last_page_ = NULL; |
| accounting_stats_.Clear(); |
| } |
| @@ -1026,14 +645,6 @@ void PagedSpace::Unprotect() { |
| #endif |
| -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. |
| @@ -1073,73 +684,7 @@ void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) { |
| } |
| -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::Expand() { |
| ASSERT(max_capacity_ % Page::kObjectAreaSize == 0); |
| ASSERT(Capacity() % Page::kObjectAreaSize == 0); |
| @@ -1147,31 +692,21 @@ bool PagedSpace::Expand(Page* last_page) { |
| 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()); |
| + 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; |
| + // We are 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 = MemoryAllocator::AllocatePages(desired_pages, &desired_pages, this); |
| + Page* p = MemoryAllocator::AllocatePage(this, executable()); |
| if (!p->is_valid()) return false; |
| - accounting_stats_.ExpandSpace(desired_pages * Page::kObjectAreaSize); |
| + accounting_stats_.ExpandSpace(Page::kObjectAreaSize); |
| ASSERT(Capacity() <= max_capacity_); |
| - MemoryAllocator::SetNextPage(last_page, p); |
| + last_page_->set_next_page(p); |
| + last_page_ = p; |
| - // 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(!last_page_->next_page()->is_valid()); |
| return true; |
| } |
| @@ -1189,58 +724,34 @@ int PagedSpace::CountTotalPages() { |
| 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 = MemoryAllocator::FreePages(top_page->next_page()); |
| - MemoryAllocator::SetNextPage(top_page, p); |
| + // TODO(gc) release half of pages? |
| + if (top_page->next_page()->is_valid()) { |
| + int pages_freed = 0; |
| + Page* page = top_page->next_page(); |
| + Page* next_page; |
| + while (page->is_valid()) { |
| + next_page = page->next_page(); |
| + MemoryAllocator::Free(page); |
| + pages_freed++; |
| + page = next_page; |
| + } |
| + top_page->set_next_page(Page::FromAddress(NULL)); |
| + last_page_ = top_page; |
| - // 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_freed * Page::kObjectAreaSize); |
| + ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize); |
| } |
| - |
| - accounting_stats_.ShrinkSpace(pages_to_free * Page::kObjectAreaSize); |
| - ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize); |
| } |
| 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 = MemoryAllocator::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 = |
| - MemoryAllocator::FindLastPageInSameChunk(last_page->next_page()); |
| - } while (Capacity() < capacity); |
| - |
| return true; |
| } |
| @@ -1258,7 +769,6 @@ void PagedSpace::Verify(ObjectVisitor* visitor) { |
| // space. |
| ASSERT(allocation_info_.VerifyPagedAllocation()); |
| Page* top_page = Page::FromAllocationTop(allocation_info_.top); |
| - ASSERT(MemoryAllocator::IsPageInSpace(top_page, this)); |
| // Loop over all the pages. |
| bool above_allocation_top = false; |
| @@ -1314,13 +824,24 @@ void PagedSpace::Verify(ObjectVisitor* visitor) { |
| // NewSpace implementation |
| -bool NewSpace::Setup(Address start, int size) { |
| +bool NewSpace::Setup(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 = 0; |
| + void* base = |
| + MemoryAllocator::ReserveAlignedMemory(2*maximum_semispace_capacity, |
| + 2*maximum_semispace_capacity, |
| + &size); |
| + |
| + if (base == NULL) return false; |
| + |
| + chunk_base_ = static_cast<Address>(base); |
| + chunk_size_ = static_cast<uintptr_t>(size); |
| + LOG(NewEvent("InitialChunk", chunk_base_, chunk_size_)); |
| ASSERT(initial_semispace_capacity <= maximum_semispace_capacity); |
| ASSERT(IsPowerOf2(maximum_semispace_capacity)); |
| @@ -1336,29 +857,28 @@ bool NewSpace::Setup(Address start, int size) { |
| #undef SET_NAME |
| #endif |
| - ASSERT(size == 2 * Heap::ReservedSemiSpaceSize()); |
| - ASSERT(IsAddressAligned(start, size, 0)); |
| + ASSERT(maximum_semispace_capacity == Heap::ReservedSemiSpaceSize()); |
| + ASSERT(static_cast<intptr_t>(chunk_size_) >= 2 * Heap::ReservedSemiSpaceSize()); |
| + ASSERT(IsAddressAligned(chunk_base_, 2*maximum_semispace_capacity, 0)); |
|
Erik Corry
2010/12/22 13:48:27
spaces around '*'
Vyacheslav Egorov (Chromium)
2010/12/22 19:55:07
Done.
|
| - 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_ + maximum_semispace_capacity, |
| initial_semispace_capacity, |
| maximum_semispace_capacity)) { |
| return false; |
| } |
| - start_ = start; |
| - address_mask_ = ~(size - 1); |
| + start_ = chunk_base_; |
| + address_mask_ = ~(2*maximum_semispace_capacity - 1); |
|
Erik Corry
2010/12/22 13:48:27
and here and a few other places
|
| 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; |
| ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); |
| return true; |
| @@ -1380,11 +900,16 @@ void NewSpace::TearDown() { |
| 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(DeleteEvent("InitialChunk", chunk_base_)); |
| + MemoryAllocator::FreeMemory(chunk_base_, |
| + static_cast<size_t>(chunk_size_), |
| + NOT_EXECUTABLE); |
| + chunk_base_ = NULL; |
| + chunk_size_ = 0; |
| } |
| @@ -1461,22 +986,6 @@ void NewSpace::ResetAllocationInfo() { |
| } |
| -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::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_); |
| -} |
| - |
| - |
| #ifdef DEBUG |
| // We do not use the SemispaceIterator because verification doesn't assume |
| // that it works (it depends on the invariants we are checking). |
| @@ -2130,68 +1639,22 @@ void FixedSizeFreeList::MarkNodes() { |
| // OldSpace implementation |
| void OldSpace::PrepareForMarkCompact(bool will_compact) { |
| + ASSERT(!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()); |
| - } |
| + // 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()); |
| // Clear the free list before a full GC---it will be rebuilt afterward. |
| free_list_.Reset(); |
| } |
| -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()); |
| - |
| - // 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()); |
| -} |
| - |
| - |
| bool NewSpace::ReserveSpace(int bytes) { |
| // We can't reliably unpack a partial snapshot that needs more new space |
| // space than the minimum NewSpace size. |
| @@ -2216,13 +1679,13 @@ void PagedSpace::FreePages(Page* prev, Page* last) { |
| first_page_ = last->next_page(); |
| } else { |
| first = prev->next_page(); |
| - MemoryAllocator::SetNextPage(prev, last->next_page()); |
| + prev->set_next_page(last->next_page()); |
| } |
| // Attach it after the last page. |
| - MemoryAllocator::SetNextPage(last_page_, first); |
| + last_page_->set_next_page(first); |
| last_page_ = last; |
| - MemoryAllocator::SetNextPage(last, NULL); |
| + last->set_next_page(NULL); |
| // Clean them up. |
| do { |
| @@ -2232,95 +1695,11 @@ void PagedSpace::FreePages(Page* prev, Page* last) { |
| 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; |
| -} |
| - |
| - |
| -void PagedSpace::RelinkPageListInChunkOrder(bool deallocate_blocks) { |
| - const bool add_to_freelist = true; |
| - |
| - // 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; |
| - |
| - 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; |
| - |
| - Page* new_last_in_use = Page::FromAddress(NULL); |
| - MemoryAllocator::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()); |
| - |
| - 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); |
| - } |
| - } |
| - |
| - // 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()); |
| - } |
| - |
| - 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()); |
| - |
| - 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); |
| - } |
| - } |
| - } |
| - |
| - page_list_is_chunk_ordered_ = true; |
| } |
| void PagedSpace::PrepareForMarkCompact(bool will_compact) { |
| - if (will_compact) { |
| - RelinkPageListInChunkOrder(false); |
| - } |
| + ASSERT(!will_compact); |
| } |
| @@ -2338,7 +1717,7 @@ bool PagedSpace::ReserveSpace(int bytes) { |
| while (bytes_left_to_reserve > 0) { |
| if (!reserved_page->next_page()->is_valid()) { |
| if (Heap::OldGenerationAllocationLimitReached()) return false; |
| - Expand(reserved_page); |
| + Expand(); |
| } |
| bytes_left_to_reserve -= Page::kPageSize; |
| reserved_page = reserved_page->next_page(); |
| @@ -2407,7 +1786,7 @@ HeapObject* OldSpace::SlowAllocateRaw(int size_in_bytes) { |
| // Try to expand the space and allocate in the new next page. |
| ASSERT(!current_page->next_page()->is_valid()); |
| - if (Expand(current_page)) { |
| + if (Expand()) { |
| return AllocateInNextPage(current_page, size_in_bytes); |
| } |
| @@ -2618,56 +1997,19 @@ void FixedSpace::PrepareForMarkCompact(bool will_compact) { |
| // Call prepare of the super class. |
| PagedSpace::PrepareForMarkCompact(will_compact); |
| - if (will_compact) { |
| - // Reset relocation info. |
| - MCResetRelocationInfo(); |
| + ASSERT(!will_compact); |
| - // 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()); |
| - } |
| + // 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. |
| @@ -2714,7 +2056,7 @@ HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) { |
| // Try to expand the space and allocate in the new next page. |
| ASSERT(!current_page->next_page()->is_valid()); |
| - if (Expand(current_page)) { |
| + if (Expand()) { |
| return AllocateInNextPage(current_page, size_in_bytes); |
| } |
| @@ -2805,14 +2147,14 @@ void CellSpace::VerifyObject(HeapObject* object) { |
| // 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; |
| } |
| @@ -2821,65 +2163,24 @@ 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; |
| - void* mem = MemoryAllocator::AllocateRawMemory(requested, &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(NewEvent("LargeObjectChunk", mem, size)); |
| - if (size < requested) { |
| - MemoryAllocator::FreeRawMemory(mem, size, executable); |
| - LOG(DeleteEvent("LargeObjectChunk", mem)); |
| - return NULL; |
| - } |
| - |
| - ObjectSpace space = (executable == EXECUTABLE) |
| - ? kObjectSpaceCodeSpace |
| - : kObjectSpaceLoSpace; |
| - MemoryAllocator::PerformAllocationCallback( |
| - space, kAllocationActionAllocate, size); |
| - |
| - LargeObjectChunk* chunk = reinterpret_cast<LargeObjectChunk*>(mem); |
| - chunk->size_ = size; |
| - return chunk; |
| -} |
| - |
| - |
| -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(AllocationSpace id) |
| : Space(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; |
| @@ -2888,19 +2189,11 @@ bool LargeObjectSpace::Setup() { |
| void LargeObjectSpace::TearDown() { |
| - while (first_chunk_ != NULL) { |
| - LargeObjectChunk* chunk = first_chunk_; |
| - first_chunk_ = first_chunk_->next(); |
| - LOG(DeleteEvent("LargeObjectChunk", chunk->address())); |
| - Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize)); |
| - Executability executable = |
| - page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE; |
| - ObjectSpace space = kObjectSpaceLoSpace; |
| - if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace; |
| - size_t size = chunk->size(); |
| - MemoryAllocator::FreeRawMemory(chunk->address(), size, executable); |
| - MemoryAllocator::PerformAllocationCallback( |
| - space, kAllocationActionFree, size); |
| + while (first_page_ != NULL) { |
| + LargePage* page = first_page_; |
| + first_page_ = first_page_->next_page(); |
| + |
| + MemoryAllocator::Free(page); |
| } |
| size_ = 0; |
| @@ -2932,87 +2225,68 @@ void LargeObjectSpace::Unprotect() { |
| #endif |
| - |
| -MaybeObject* LargeObjectSpace::AllocateRawInternal(int requested_size, |
| - int object_size, |
| +MaybeObject* LargeObjectSpace::AllocateRawInternal(int object_size, |
| Executability executable) { |
| - ASSERT(0 < object_size && object_size <= requested_size); |
| - |
| // Check if we want to force a GC before growing the old space further. |
| // If so, fail the allocation. |
| if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { |
| return Failure::RetryAfterGC(identity()); |
| } |
| - LargeObjectChunk* chunk = LargeObjectChunk::New(requested_size, executable); |
| - if (chunk == NULL) { |
| - return Failure::RetryAfterGC(identity()); |
| - } |
| + LargePage* page = MemoryAllocator::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; |
| - |
| - // Initialize page header. |
| - Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize)); |
| - Address object_address = page->ObjectAreaStart(); |
| + page->set_next_page(first_page_); |
| + first_page_ = page; |
| - // 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->SetIsPageExecutable(executable); |
| - page->SetRegionMarks(Page::kAllRegionsCleanMarks); |
| - return HeapObject::FromAddress(object_address); |
| + return page->GetObject(); |
| } |
| MaybeObject* LargeObjectSpace::AllocateRawCode(int size_in_bytes) { |
| ASSERT(0 < size_in_bytes); |
| - return AllocateRawInternal(size_in_bytes, |
| - size_in_bytes, |
| - EXECUTABLE); |
| + return AllocateRawInternal(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); |
| + return AllocateRawInternal(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); |
| + return AllocateRawInternal(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; |
| @@ -3031,95 +2305,47 @@ void LargeObjectSpace::IterateDirtyRegions(ObjectSlotCallback copy_object) { |
| 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); |
| - } |
| + // TODO(gc): we can no longer assume that LargePage is bigger than normal |
| + // page. |
| + ASSERT(marks == Page::kAllRegionsDirtyMarks); |
| + USE(marks); |
| - page->SetRegionMarks(newmarks); |
| - } |
| + Address start = object->address(); |
| + Address object_end = start + object->Size(); |
| + Heap::IteratePointersInDirtyRegion(start, object_end, copy_object); |
| } |
| } |
| } |
| 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(); |
| MarkCompactCollector::tracer()->decrement_marked_count(); |
| previous = current; |
| - current = current->next(); |
| + current = current->next_page(); |
| } else { |
| - Page* page = Page::FromAddress(RoundUp(current->address(), |
| - Page::kPageSize)); |
| - Executability executable = |
| - page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE; |
| - Address chunk_address = current->address(); |
| - size_t chunk_size = current->size(); |
| - |
| + LargePage* page = current; |
| // Cut the chunk out from the chunk list. |
| - 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. |
| MarkCompactCollector::ReportDeleteIfNeeded(object); |
| - size_ -= static_cast<int>(chunk_size); |
| + size_ -= static_cast<int>(page->size()); |
| objects_size_ -= object->Size(); |
| page_count_--; |
| - ObjectSpace space = kObjectSpaceLoSpace; |
| - if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace; |
| - MemoryAllocator::FreeRawMemory(chunk_address, chunk_size, executable); |
| - MemoryAllocator::PerformAllocationCallback(space, kAllocationActionFree, |
| - size_); |
| - LOG(DeleteEvent("LargeObjectChunk", chunk_address)); |
| + |
| + MemoryAllocator::Free(page); |
| } |
| } |
| } |
| @@ -3130,12 +2356,14 @@ bool LargeObjectSpace::Contains(HeapObject* object) { |
| if (Heap::new_space()->Contains(address)) { |
| return false; |
| } |
| - Page* page = Page::FromAddress(address); |
| + MemoryChunk* chunk = MemoryChunk::FromAddress(address); |
| + |
| + bool owned = chunk->owner() == this; |
| - SLOW_ASSERT(!page->IsLargeObjectPage() |
| + SLOW_ASSERT(!owned |
| || !FindObject(address)->IsFailure()); |
| - return page->IsLargeObjectPage(); |
| + return owned; |
| } |
| @@ -3143,9 +2371,9 @@ bool LargeObjectSpace::Contains(HeapObject* object) { |
| // 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(); |