| Index: src/spaces.cc
|
| diff --git a/src/spaces.cc b/src/spaces.cc
|
| index 46f868f70ff7470db3ab5ba3125c31cee2e05dae..739154ca55c23474685ad77457b336df08d1e774 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
|
| @@ -168,7 +165,12 @@ bool CodeRange::Setup(const size_t requested) {
|
| // We are sure that we have mapped a block of requested addresses.
|
| ASSERT(code_range_->size() == requested);
|
| LOG(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);
|
| + int size = code_range_->size() - (aligned_base - base);
|
| + allocation_list_.Add(FreeBlock(aligned_base, size));
|
| current_allocation_block_index_ = 0;
|
| return true;
|
| }
|
| @@ -225,7 +227,8 @@ void CodeRange::GetNextAllocationBlock(size_t requested) {
|
|
|
|
|
|
|
| -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
|
| @@ -233,13 +236,16 @@ 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);
|
| + 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;
|
| @@ -253,7 +259,8 @@ void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) {
|
| }
|
|
|
|
|
| -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);
|
| }
|
| @@ -270,316 +277,220 @@ 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() {
|
| + // Check that spaces were teared down before MemoryAllocator.
|
| + ASSERT(size_ == 0);
|
| + ASSERT(size_executable_ == 0);
|
| + capacity_ = 0;
|
| + capacity_executable_ = 0;
|
| }
|
|
|
|
|
| -void MemoryAllocator::TearDown() {
|
| - for (int i = 0; i < max_nof_chunks_; i++) {
|
| - if (chunks_[i].address() != NULL) DeleteChunk(i);
|
| +void MemoryAllocator::FreeMemory(Address 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;
|
| - }
|
| +Address MemoryAllocator::ReserveAlignedMemory(const size_t requested,
|
| + size_t alignment,
|
| + size_t* allocated_size) {
|
| + ASSERT(IsAligned(alignment, OS::AllocateAlignment()));
|
| + if (size_ + requested > capacity_) return NULL;
|
| +
|
| + size_t allocated = RoundUp(requested + alignment, OS::AllocateAlignment());
|
| +
|
| + Address base = reinterpret_cast<Address>(
|
| + VirtualMemory::ReserveRegion(allocated));
|
| +
|
| + Address end = base + allocated;
|
| +
|
| + if (base == 0) return NULL;
|
| +
|
| + Address 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);
|
| +
|
| + Address requested_end = base + requested;
|
| + Address 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 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_)) {
|
| +Address MemoryAllocator::AllocateAlignedMemory(const size_t requested,
|
| + size_t alignment,
|
| + Executability executable,
|
| + size_t* allocated_size) {
|
| + Address 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;
|
| + Address 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(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(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(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 +500,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 +513,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 +569,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 +582,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 +615,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 +648,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 +687,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 +695,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 +727,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 +772,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 +827,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;
|
| + Address base =
|
| + MemoryAllocator::ReserveAlignedMemory(2 * maximum_semispace_capacity,
|
| + 2 * maximum_semispace_capacity,
|
| + &size);
|
| +
|
| + if (base == NULL) return false;
|
| +
|
| + chunk_base_ = 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 +860,29 @@ 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));
|
|
|
| - 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);
|
| 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 +904,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 +990,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 +1643,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 +1683,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 +1699,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 +1721,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 +1790,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 +2001,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 +2060,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 +2151,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 +2167,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 +2193,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 +2229,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 +2309,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 +2360,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 +2375,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();
|
|
|