Index: src/heap/spaces.h |
diff --git a/src/heap/spaces.h b/src/heap/spaces.h |
index c468ca4d87ff8964e4a8c9fbaf8d0854571b335c..2a1474da98a67db3bccc217059d71d4f1baf645b 100644 |
--- a/src/heap/spaces.h |
+++ b/src/heap/spaces.h |
@@ -5,6 +5,8 @@ |
#ifndef V8_HEAP_SPACES_H_ |
#define V8_HEAP_SPACES_H_ |
+#include <list> |
+ |
#include "src/allocation.h" |
#include "src/atomic-utils.h" |
#include "src/base/atomicops.h" |
@@ -440,6 +442,10 @@ class MemoryChunk { |
// still has to be performed. |
PRE_FREED, |
+ // |POOLED|: When actually freeing this chunk, only uncommit and do not |
+ // give up the reservation as we still reuse the chunk at some point. |
+ POOLED, |
+ |
// |COMPACTION_WAS_ABORTED|: Indicates that the compaction in this page |
// has been aborted and needs special handling by the sweeper. |
COMPACTION_WAS_ABORTED, |
@@ -1265,15 +1271,92 @@ class SkipList { |
// A space acquires chunks of memory from the operating system. The memory |
// allocator allocated and deallocates pages for the paged heap spaces and large |
// pages for large object space. |
-// |
-// Each space has to manage it's own pages. |
-// |
class MemoryAllocator { |
public: |
+ // Unmapper takes care of concurrently unmapping and uncommitting memory |
+ // chunks. |
+ class Unmapper { |
+ public: |
+ class UnmapFreeMemoryTask; |
+ |
+ explicit Unmapper(MemoryAllocator* allocator) |
+ : allocator_(allocator), |
+ pending_unmapping_tasks_semaphore_(0), |
+ concurrent_unmapping_tasks_active_(0) {} |
+ |
+ void AddMemoryChunkSafe(MemoryChunk* chunk) { |
+ if ((chunk->size() == Page::kPageSize) && |
+ (chunk->executable() != EXECUTABLE)) { |
+ AddMemoryChunkSafe<kRegular>(chunk); |
+ } else { |
+ AddMemoryChunkSafe<kNonRegular>(chunk); |
+ } |
+ } |
+ |
+ MemoryChunk* TryGetPooledMemoryChunkSafe() { |
+ // Procedure: |
+ // (1) Try to get a chunk that was declared as pooled and already has |
+ // been uncommitted. |
+ // (2) Try to steal any memory chunk of kPageSize that would've been |
+ // unmapped. |
+ MemoryChunk* chunk = GetMemoryChunkSafe<kPooled>(); |
+ if (chunk == nullptr) { |
+ chunk = GetMemoryChunkSafe<kRegular>(); |
+ if (chunk != nullptr) { |
+ // For stolen chunks we need to manually free any allocated memory. |
+ chunk->ReleaseAllocatedMemory(); |
+ } |
+ } |
+ return chunk; |
+ } |
+ |
+ void FreeQueuedChunks(); |
+ bool WaitUntilCompleted(); |
+ |
+ private: |
+ enum ChunkQueueType { |
+ kRegular, // Pages of kPageSize that do not live in a CodeRange and |
+ // can thus be used for stealing. |
+ kNonRegular, // Large chunks and executable chunks. |
+ kPooled, // Pooled chunks, already uncommited and ready for reuse. |
+ kNumberOfChunkQueues, |
+ }; |
+ |
+ template <ChunkQueueType type> |
+ void AddMemoryChunkSafe(MemoryChunk* chunk) { |
+ base::LockGuard<base::Mutex> guard(&mutex_); |
+ chunks_[type].push_back(chunk); |
+ } |
+ |
+ template <ChunkQueueType type> |
+ MemoryChunk* GetMemoryChunkSafe() { |
+ base::LockGuard<base::Mutex> guard(&mutex_); |
+ if (chunks_[type].empty()) return nullptr; |
+ MemoryChunk* chunk = chunks_[type].front(); |
+ chunks_[type].pop_front(); |
+ return chunk; |
+ } |
+ |
+ void PerformFreeMemoryOnQueuedChunks(); |
+ |
+ base::Mutex mutex_; |
+ MemoryAllocator* allocator_; |
+ std::list<MemoryChunk*> chunks_[kNumberOfChunkQueues]; |
+ base::Semaphore pending_unmapping_tasks_semaphore_; |
+ intptr_t concurrent_unmapping_tasks_active_; |
+ |
+ friend class MemoryAllocator; |
+ }; |
+ |
enum AllocationMode { |
kRegular, |
kPooled, |
}; |
+ enum FreeMode { |
+ kFull, |
+ kPreFreeAndQueue, |
+ kPooledAndQueue, |
+ }; |
explicit MemoryAllocator(Isolate* isolate); |
@@ -1294,16 +1377,7 @@ class MemoryAllocator { |
LargePage* AllocateLargePage(intptr_t size, LargeObjectSpace* owner, |
Executability executable); |
- // PreFree logically frees the object, i.e., it takes care of the size |
- // bookkeeping and calls the allocation callback. |
- void PreFreeMemory(MemoryChunk* chunk); |
- |
- // FreeMemory can be called concurrently when PreFree was executed before. |
- void PerformFreeMemory(MemoryChunk* chunk); |
- |
- // Free is a wrapper method. For kRegular AllocationMode it calls PreFree and |
- // PerformFreeMemory together. For kPooled it will dispatch to pooled free. |
- template <MemoryAllocator::AllocationMode mode = kRegular> |
+ template <MemoryAllocator::FreeMode mode = kFull> |
void Free(MemoryChunk* chunk); |
// Returns allocated spaces in bytes. |
@@ -1409,16 +1483,21 @@ class MemoryAllocator { |
size_t reserved_size); |
CodeRange* code_range() { return code_range_; } |
+ Unmapper* unmapper() { return &unmapper_; } |
private: |
+ // PreFree logically frees the object, i.e., it takes care of the size |
+ // bookkeeping and calls the allocation callback. |
+ void PreFreeMemory(MemoryChunk* chunk); |
+ |
+ // FreeMemory can be called concurrently when PreFree was executed before. |
+ void PerformFreeMemory(MemoryChunk* chunk); |
+ |
// See AllocatePage for public interface. Note that currently we only support |
// pools for NOT_EXECUTABLE pages of size MemoryChunk::kPageSize. |
template <typename SpaceType> |
MemoryChunk* AllocatePagePooled(SpaceType* owner); |
- // Free that chunk into the pool. |
- void FreePooled(MemoryChunk* chunk); |
- |
Isolate* isolate_; |
CodeRange* code_range_; |
@@ -1474,9 +1553,8 @@ class MemoryAllocator { |
} while ((high > ptr) && !highest_ever_allocated_.TrySetValue(ptr, high)); |
} |
- List<MemoryChunk*> chunk_pool_; |
- |
base::VirtualMemory last_chunk_; |
+ Unmapper unmapper_; |
friend class TestCodeRangeScope; |