Chromium Code Reviews| Index: src/heap/spaces.cc |
| diff --git a/src/heap/spaces.cc b/src/heap/spaces.cc |
| index ff5a704e49aab49b030295a7a3584b6ec5caa9ba..7dea0f0b40c995d0ab097dbf5b1a0ea7bb2fa042 100644 |
| --- a/src/heap/spaces.cc |
| +++ b/src/heap/spaces.cc |
| @@ -6,11 +6,13 @@ |
| #include "src/base/bits.h" |
| #include "src/base/platform/platform.h" |
| +#include "src/base/platform/semaphore.h" |
| #include "src/full-codegen/full-codegen.h" |
| #include "src/heap/slot-set.h" |
| #include "src/macro-assembler.h" |
| #include "src/msan.h" |
| #include "src/snapshot/snapshot.h" |
| +#include "src/v8.h" |
| namespace v8 { |
| namespace internal { |
| @@ -303,7 +305,8 @@ MemoryAllocator::MemoryAllocator(Isolate* isolate) |
| size_(0), |
| size_executable_(0), |
| lowest_ever_allocated_(reinterpret_cast<void*>(-1)), |
| - highest_ever_allocated_(reinterpret_cast<void*>(0)) {} |
| + highest_ever_allocated_(reinterpret_cast<void*>(0)), |
| + unmapper_(this) {} |
| bool MemoryAllocator::SetUp(intptr_t capacity, intptr_t capacity_executable, |
| intptr_t code_range_size) { |
| @@ -322,10 +325,14 @@ bool MemoryAllocator::SetUp(intptr_t capacity, intptr_t capacity_executable, |
| void MemoryAllocator::TearDown() { |
| - for (MemoryChunk* chunk : chunk_pool_) { |
| + unmapper()->WaitUntilCompleted(); |
| + |
| + MemoryChunk* chunk = nullptr; |
| + while ((chunk = unmapper()->TryGetPooledMemoryChunkSafe()) != nullptr) { |
| FreeMemory(reinterpret_cast<Address>(chunk), MemoryChunk::kPageSize, |
| NOT_EXECUTABLE); |
| } |
| + |
| // Check that spaces were torn down before MemoryAllocator. |
| DCHECK_EQ(size_.Value(), 0); |
| // TODO(gc) this will be true again when we fix FreeMemory. |
| @@ -341,6 +348,55 @@ void MemoryAllocator::TearDown() { |
| code_range_ = nullptr; |
| } |
| +class MemoryAllocator::Unmapper::UnmapFreeMemoryTask : public v8::Task { |
| + public: |
| + explicit UnmapFreeMemoryTask(Unmapper* unmapper) : unmapper_(unmapper) {} |
| + |
| + private: |
| + // v8::Task overrides. |
| + void Run() override { |
| + unmapper_->PerformFreeMemoryOnQueuedChunks(); |
| + unmapper_->pending_unmapping_tasks_semaphore_.Signal(); |
| + } |
| + |
| + Unmapper* unmapper_; |
| + DISALLOW_COPY_AND_ASSIGN(UnmapFreeMemoryTask); |
| +}; |
| + |
| +void MemoryAllocator::Unmapper::FreeQueuedChunks() { |
| + if (FLAG_concurrent_sweeping) { |
| + V8::GetCurrentPlatform()->CallOnBackgroundThread( |
| + new UnmapFreeMemoryTask(this), v8::Platform::kShortRunningTask); |
| + concurrent_unmapping_tasks_active_++; |
| + } else { |
| + PerformFreeMemoryOnQueuedChunks(); |
| + } |
| +} |
| + |
| +bool MemoryAllocator::Unmapper::WaitUntilCompleted() { |
| + bool waited = false; |
| + while (concurrent_unmapping_tasks_active_ > 0) { |
| + pending_unmapping_tasks_semaphore_.Wait(); |
| + concurrent_unmapping_tasks_active_--; |
| + waited = true; |
| + } |
| + return waited; |
| +} |
| + |
| +void MemoryAllocator::Unmapper::PerformFreeMemoryOnQueuedChunks() { |
| + MemoryChunk* chunk = nullptr; |
| + // Regular chunks. |
| + while ((chunk = GetMemoryChunkSafe<kRegularProcess>()) != nullptr) { |
| + bool pooled = chunk->IsFlagSet(MemoryChunk::POOLED); |
| + allocator_->PerformFreeMemory(chunk); |
| + if (pooled) AddMemoryChunkSafe<kPooledOutput>(chunk); |
| + } |
| + // Large chunks. |
|
Hannes Payer (out of office)
2016/04/26 13:01:27
This is not just large junks but also code.
Can w
Michael Lippautz
2016/04/26 13:57:51
As discussed offline: Let's keep the order. Fixed
|
| + while ((chunk = GetMemoryChunkSafe<kNonRegularProcess>()) != nullptr) { |
| + allocator_->PerformFreeMemory(chunk); |
| + } |
| +} |
| + |
| bool MemoryAllocator::CommitMemory(Address base, size_t size, |
| Executability executable) { |
| if (!base::VirtualMemory::CommitRegion(base, size, |
| @@ -748,28 +804,45 @@ void MemoryAllocator::PerformFreeMemory(MemoryChunk* chunk) { |
| chunk->ReleaseAllocatedMemory(); |
| base::VirtualMemory* reservation = chunk->reserved_memory(); |
| - if (reservation->IsReserved()) { |
| - FreeMemory(reservation, chunk->executable()); |
| + if (chunk->IsFlagSet(MemoryChunk::POOLED)) { |
| + UncommitBlock(reinterpret_cast<Address>(chunk), MemoryChunk::kPageSize); |
| } else { |
| - FreeMemory(chunk->address(), chunk->size(), chunk->executable()); |
| + if (reservation->IsReserved()) { |
| + FreeMemory(reservation, chunk->executable()); |
| + } else { |
| + FreeMemory(chunk->address(), chunk->size(), chunk->executable()); |
| + } |
| } |
| } |
| -template <MemoryAllocator::AllocationMode mode> |
| +template <MemoryAllocator::FreeMode mode> |
| void MemoryAllocator::Free(MemoryChunk* chunk) { |
| - if (mode == kRegular) { |
| - PreFreeMemory(chunk); |
| - PerformFreeMemory(chunk); |
| - } else { |
| - DCHECK_EQ(mode, kPooled); |
| - FreePooled(chunk); |
| + switch (mode) { |
| + case kFull: |
| + PreFreeMemory(chunk); |
| + PerformFreeMemory(chunk); |
| + break; |
| + case kPooledAndQueue: |
| + DCHECK_EQ(chunk->size(), static_cast<size_t>(MemoryChunk::kPageSize)); |
| + DCHECK_EQ(chunk->executable(), NOT_EXECUTABLE); |
| + chunk->SetFlag(MemoryChunk::POOLED); |
| + // Fall through to kPreFreeAndQueue. |
| + case kPreFreeAndQueue: |
| + PreFreeMemory(chunk); |
| + // The chunks added to this queue will be freed by a concurrent thread. |
| + unmapper()->AddMemoryChunkSafe(chunk); |
| + break; |
| + default: |
| + UNREACHABLE(); |
| } |
| } |
| -template void MemoryAllocator::Free<MemoryAllocator::kRegular>( |
| +template void MemoryAllocator::Free<MemoryAllocator::kFull>(MemoryChunk* chunk); |
| + |
| +template void MemoryAllocator::Free<MemoryAllocator::kPreFreeAndQueue>( |
| MemoryChunk* chunk); |
| -template void MemoryAllocator::Free<MemoryAllocator::kPooled>( |
| +template void MemoryAllocator::Free<MemoryAllocator::kPooledAndQueue>( |
| MemoryChunk* chunk); |
| template <typename PageType, MemoryAllocator::AllocationMode mode, |
| @@ -803,9 +876,9 @@ template NewSpacePage* MemoryAllocator::AllocatePage< |
| template <typename SpaceType> |
| MemoryChunk* MemoryAllocator::AllocatePagePooled(SpaceType* owner) { |
| - if (chunk_pool_.is_empty()) return nullptr; |
| + MemoryChunk* chunk = unmapper()->TryGetPooledMemoryChunkSafe(); |
| + if (chunk == nullptr) return nullptr; |
| const int size = MemoryChunk::kPageSize; |
| - MemoryChunk* chunk = chunk_pool_.RemoveLast(); |
| const Address start = reinterpret_cast<Address>(chunk); |
| const Address area_start = start + MemoryChunk::kObjectStartOffset; |
| const Address area_end = start + size; |
| @@ -817,18 +890,6 @@ MemoryChunk* MemoryAllocator::AllocatePagePooled(SpaceType* owner) { |
| return chunk; |
| } |
| -void MemoryAllocator::FreePooled(MemoryChunk* chunk) { |
| - DCHECK_EQ(chunk->size(), static_cast<size_t>(MemoryChunk::kPageSize)); |
| - DCHECK_EQ(chunk->executable(), NOT_EXECUTABLE); |
| - chunk_pool_.Add(chunk); |
| - intptr_t chunk_size = static_cast<intptr_t>(chunk->size()); |
| - if (chunk->executable() == EXECUTABLE) { |
| - size_executable_.Increment(-chunk_size); |
| - } |
| - size_.Increment(-chunk_size); |
| - UncommitBlock(reinterpret_cast<Address>(chunk), MemoryChunk::kPageSize); |
| -} |
| - |
| bool MemoryAllocator::CommitBlock(Address start, size_t size, |
| Executability executable) { |
| if (!CommitMemory(start, size, executable)) return false; |
| @@ -971,12 +1032,16 @@ bool MemoryAllocator::CommitExecutableMemory(base::VirtualMemory* vm, |
| // MemoryChunk implementation |
| void MemoryChunk::ReleaseAllocatedMemory() { |
| - delete skip_list_; |
| - skip_list_ = nullptr; |
| - delete mutex_; |
| - mutex_ = nullptr; |
| - ReleaseOldToNewSlots(); |
| - ReleaseOldToOldSlots(); |
| + if (skip_list_ != nullptr) { |
| + delete skip_list_; |
| + skip_list_ = nullptr; |
| + } |
| + if (mutex_ != nullptr) { |
| + delete mutex_; |
| + mutex_ = nullptr; |
| + } |
| + if (old_to_new_slots_ != nullptr) ReleaseOldToNewSlots(); |
| + if (old_to_old_slots_ != nullptr) ReleaseOldToOldSlots(); |
| } |
| static SlotSet* AllocateSlotSet(size_t size, Address page_start) { |
| @@ -1060,7 +1125,7 @@ bool PagedSpace::HasBeenSetUp() { return true; } |
| void PagedSpace::TearDown() { |
| PageIterator iterator(this); |
| while (iterator.has_next()) { |
| - heap()->memory_allocator()->Free(iterator.next()); |
| + heap()->memory_allocator()->Free<MemoryAllocator::kFull>(iterator.next()); |
| } |
| anchor_.set_next_page(&anchor_); |
| anchor_.set_prev_page(&anchor_); |
| @@ -1251,7 +1316,7 @@ void PagedSpace::ReleasePage(Page* page) { |
| } |
| AccountUncommitted(static_cast<intptr_t>(page->size())); |
| - heap()->QueueMemoryChunkForFree(page); |
| + heap()->memory_allocator()->Free<MemoryAllocator::kPreFreeAndQueue>(page); |
| DCHECK(Capacity() > 0); |
| accounting_stats_.ShrinkSpace(AreaSize()); |
| @@ -1715,12 +1780,14 @@ bool SemiSpace::Uncommit() { |
| DCHECK(is_committed()); |
| NewSpacePageIterator it(this); |
| while (it.has_next()) { |
| - heap()->memory_allocator()->Free<MemoryAllocator::kPooled>(it.next()); |
| + heap()->memory_allocator()->Free<MemoryAllocator::kPooledAndQueue>( |
| + it.next()); |
| } |
| anchor()->set_next_page(anchor()); |
| anchor()->set_prev_page(anchor()); |
| AccountUncommitted(current_capacity_); |
| committed_ = false; |
| + heap()->memory_allocator()->unmapper()->FreeQueuedChunks(); |
| return true; |
| } |
| @@ -1798,10 +1865,12 @@ bool SemiSpace::ShrinkTo(int new_capacity) { |
| new_last_page = last_page->prev_page(); |
| new_last_page->set_next_page(anchor()); |
| anchor()->set_prev_page(new_last_page); |
| - heap()->memory_allocator()->Free<MemoryAllocator::kPooled>(last_page); |
| + heap()->memory_allocator()->Free<MemoryAllocator::kPooledAndQueue>( |
| + last_page); |
| delta_pages--; |
| } |
| AccountUncommitted(static_cast<intptr_t>(delta)); |
| + heap()->memory_allocator()->unmapper()->FreeQueuedChunks(); |
| } |
| current_capacity_ = new_capacity; |
| return true; |
| @@ -2894,7 +2963,7 @@ void LargeObjectSpace::TearDown() { |
| ObjectSpace space = static_cast<ObjectSpace>(1 << identity()); |
| heap()->memory_allocator()->PerformAllocationCallback( |
| space, kAllocationActionFree, page->size()); |
| - heap()->memory_allocator()->Free(page); |
| + heap()->memory_allocator()->Free<MemoryAllocator::kFull>(page); |
| } |
| SetUp(); |
| } |
| @@ -3037,7 +3106,7 @@ void LargeObjectSpace::FreeUnmarkedObjects() { |
| static_cast<uint32_t>(key)); |
| } |
| - heap()->QueueMemoryChunkForFree(page); |
| + heap()->memory_allocator()->Free<MemoryAllocator::kPreFreeAndQueue>(page); |
| } |
| } |
| } |