| Index: src/heap/spaces.h
|
| diff --git a/src/heap/spaces.h b/src/heap/spaces.h
|
| index 8ac8103f17d98f5b2ce52b73a176b41b5829b02b..98cc365b81d1200bd6b364c41220210493e66fcf 100644
|
| --- a/src/heap/spaces.h
|
| +++ b/src/heap/spaces.h
|
| @@ -27,6 +27,7 @@ class FreeList;
|
| class Isolate;
|
| class MemoryAllocator;
|
| class MemoryChunk;
|
| +class Page;
|
| class PagedSpace;
|
| class SemiSpace;
|
| class SkipList;
|
| @@ -287,6 +288,113 @@ class Bitmap {
|
| }
|
| };
|
|
|
| +enum FreeListCategoryType {
|
| + kTiniest,
|
| + kTiny,
|
| + kSmall,
|
| + kMedium,
|
| + kLarge,
|
| + kHuge,
|
| +
|
| + kFirstCategory = kTiniest,
|
| + kLastCategory = kHuge,
|
| + kNumberOfCategories = kLastCategory + 1,
|
| + kInvalidCategory
|
| +};
|
| +
|
| +enum FreeMode { kLinkCategory, kDoNotLinkCategory };
|
| +
|
| +// A free list category maintains a linked list of free memory blocks.
|
| +class FreeListCategory {
|
| + public:
|
| + static const int kSize = kIntSize + // FreeListCategoryType type_
|
| + kIntSize + // int available_
|
| + kPointerSize + // FreeSpace* top_
|
| + kPointerSize + // FreeListCategory* prev_
|
| + kPointerSize; // FreeListCategory* next_
|
| +
|
| + FreeListCategory()
|
| + : type_(kInvalidCategory),
|
| + available_(0),
|
| + top_(nullptr),
|
| + prev_(nullptr),
|
| + next_(nullptr) {}
|
| +
|
| + void Initialize(FreeListCategoryType type) {
|
| + type_ = type;
|
| + available_ = 0;
|
| + top_ = nullptr;
|
| + prev_ = nullptr;
|
| + next_ = nullptr;
|
| + }
|
| +
|
| + void Invalidate();
|
| +
|
| + void Reset();
|
| +
|
| + void ResetStats() { Reset(); }
|
| +
|
| + void RepairFreeList(Heap* heap);
|
| +
|
| + // Relinks the category into the currently owning free list. Requires that the
|
| + // category is currently unlinked.
|
| + void Relink();
|
| +
|
| + bool Free(FreeSpace* node, int size_in_bytes, FreeMode mode);
|
| +
|
| + // Picks a node from the list and stores its size in |node_size|. Returns
|
| + // nullptr if the category is empty.
|
| + FreeSpace* PickNodeFromList(int* node_size);
|
| +
|
| + // Performs a single try to pick a node of at least |minimum_size| from the
|
| + // category. Stores the actual size in |node_size|. Returns nullptr if no
|
| + // node is found.
|
| + FreeSpace* TryPickNodeFromList(int minimum_size, int* node_size);
|
| +
|
| + // Picks a node of at least |minimum_size| from the category. Stores the
|
| + // actual size in |node_size|. Returns nullptr if no node is found.
|
| + FreeSpace* SearchForNodeInList(int minimum_size, int* node_size);
|
| +
|
| + inline FreeList* owner();
|
| + inline bool is_linked();
|
| + bool is_empty() { return top() == nullptr; }
|
| + int available() const { return available_; }
|
| +
|
| +#ifdef DEBUG
|
| + intptr_t SumFreeList();
|
| + int FreeListLength();
|
| +#endif
|
| +
|
| + private:
|
| + // For debug builds we accurately compute free lists lengths up until
|
| + // {kVeryLongFreeList} by manually walking the list.
|
| + static const int kVeryLongFreeList = 500;
|
| +
|
| + inline Page* page();
|
| +
|
| + FreeSpace* top() { return top_; }
|
| + void set_top(FreeSpace* top) { top_ = top; }
|
| + FreeListCategory* prev() { return prev_; }
|
| + void set_prev(FreeListCategory* prev) { prev_ = prev; }
|
| + FreeListCategory* next() { return next_; }
|
| + void set_next(FreeListCategory* next) { next_ = next; }
|
| +
|
| + // |type_|: The type of this free list category.
|
| + FreeListCategoryType type_;
|
| +
|
| + // |available_|: Total available bytes in all blocks of this free list
|
| + // category.
|
| + int available_;
|
| +
|
| + // |top_|: Points to the top FreeSpace* in the free list category.
|
| + FreeSpace* top_;
|
| +
|
| + FreeListCategory* prev_;
|
| + FreeListCategory* next_;
|
| +
|
| + friend class FreeList;
|
| + friend class PagedSpace;
|
| +};
|
|
|
| // MemoryChunk represents a memory region owned by a specific space.
|
| // It is divided into the header and the body. Chunk start is always
|
| @@ -397,7 +505,9 @@ class MemoryChunk {
|
| + kPointerSize // base::AtomicWord concurrent_sweeping_
|
| + 2 * kPointerSize // AtomicNumber free-list statistics
|
| + kPointerSize // AtomicValue next_chunk_
|
| - + kPointerSize; // AtomicValue prev_chunk_
|
| + + kPointerSize // AtomicValue prev_chunk_
|
| + // FreeListCategory categories_[kNumberOfCategories]
|
| + + FreeListCategory::kSize * kNumberOfCategories;
|
|
|
| // We add some more space to the computed header size to amount for missing
|
| // alignment requirements in our computation.
|
| @@ -577,19 +687,6 @@ class MemoryChunk {
|
| return !IsEvacuationCandidate() && !IsFlagSet(NEVER_ALLOCATE_ON_PAGE);
|
| }
|
|
|
| - void MarkEvacuationCandidate() {
|
| - DCHECK(!IsFlagSet(NEVER_EVACUATE));
|
| - DCHECK_NULL(old_to_old_slots_);
|
| - DCHECK_NULL(typed_old_to_old_slots_);
|
| - SetFlag(EVACUATION_CANDIDATE);
|
| - }
|
| -
|
| - void ClearEvacuationCandidate() {
|
| - DCHECK_NULL(old_to_old_slots_);
|
| - DCHECK_NULL(typed_old_to_old_slots_);
|
| - ClearFlag(EVACUATION_CANDIDATE);
|
| - }
|
| -
|
| bool ShouldSkipEvacuationSlotRecording() {
|
| return (flags_ & kSkipEvacuationSlotsRecordingMask) != 0;
|
| }
|
| @@ -699,6 +796,8 @@ class MemoryChunk {
|
| // prev_chunk_ holds a pointer of type MemoryChunk
|
| AtomicValue<MemoryChunk*> prev_chunk_;
|
|
|
| + FreeListCategory categories_[kNumberOfCategories];
|
| +
|
| private:
|
| void InitializeReservedMemory() { reservation_.Reset(); }
|
|
|
| @@ -706,19 +805,6 @@ class MemoryChunk {
|
| friend class MemoryChunkValidator;
|
| };
|
|
|
| -enum FreeListCategoryType {
|
| - kTiniest,
|
| - kTiny,
|
| - kSmall,
|
| - kMedium,
|
| - kLarge,
|
| - kHuge,
|
| -
|
| - kFirstCategory = kTiniest,
|
| - kLastCategory = kHuge,
|
| - kNumberOfCategories = kLastCategory + 1
|
| -};
|
| -
|
| // -----------------------------------------------------------------------------
|
| // A page is a memory chunk of a size 1MB. Large object pages may be larger.
|
| //
|
| @@ -822,6 +908,17 @@ class Page : public MemoryChunk {
|
| available_in_free_list());
|
| }
|
|
|
| + template <typename Callback>
|
| + inline void ForAllFreeListCategories(Callback callback) {
|
| + for (int i = kFirstCategory; i < kNumberOfCategories; i++) {
|
| + callback(&categories_[i]);
|
| + }
|
| + }
|
| +
|
| + FreeListCategory* free_list_category(FreeListCategoryType type) {
|
| + return &categories_[type];
|
| + }
|
| +
|
| #define FRAGMENTATION_STATS_ACCESSORS(type, name) \
|
| type name() { return name##_.Value(); } \
|
| void set_##name(type name) { name##_.SetValue(name); } \
|
| @@ -836,6 +933,13 @@ class Page : public MemoryChunk {
|
| void Print();
|
| #endif // DEBUG
|
|
|
| + inline void MarkNeverAllocateForTesting();
|
| + inline void MarkEvacuationCandidate();
|
| + inline void ClearEvacuationCandidate();
|
| +
|
| + private:
|
| + inline void InitializeFreeListCategories();
|
| +
|
| friend class MemoryAllocator;
|
| };
|
|
|
| @@ -1475,12 +1579,6 @@ class AllocationStats BASE_EMBEDDED {
|
|
|
| void ClearSize() { size_ = capacity_; }
|
|
|
| - // Reset the allocation statistics (i.e., available = capacity with no wasted
|
| - // or allocated bytes).
|
| - void Reset() {
|
| - size_ = 0;
|
| - }
|
| -
|
| // Accessors for the allocation statistics.
|
| intptr_t Capacity() { return capacity_; }
|
| intptr_t MaxCapacity() { return max_capacity_; }
|
| @@ -1507,13 +1605,13 @@ class AllocationStats BASE_EMBEDDED {
|
| void ShrinkSpace(int size_in_bytes) {
|
| capacity_ -= size_in_bytes;
|
| size_ -= size_in_bytes;
|
| - CHECK(size_ >= 0);
|
| + CHECK_GE(size_, 0);
|
| }
|
|
|
| // Allocate from available bytes (available -> size).
|
| void AllocateBytes(intptr_t size_in_bytes) {
|
| size_ += size_in_bytes;
|
| - CHECK(size_ >= 0);
|
| + CHECK_GE(size_, 0);
|
| }
|
|
|
| // Free allocated bytes, making them available (size -> available).
|
| @@ -1552,80 +1650,6 @@ class AllocationStats BASE_EMBEDDED {
|
| intptr_t size_;
|
| };
|
|
|
| -
|
| -// A free list category maintains a linked list of free memory blocks.
|
| -class FreeListCategory {
|
| - public:
|
| - FreeListCategory() : top_(nullptr), end_(nullptr), available_(0) {}
|
| -
|
| - void Initialize(FreeList* owner, FreeListCategoryType type) {
|
| - owner_ = owner;
|
| - type_ = type;
|
| - }
|
| -
|
| - // Concatenates {category} into {this}.
|
| - //
|
| - // Note: Thread-safe.
|
| - intptr_t Concatenate(FreeListCategory* category);
|
| -
|
| - void Reset();
|
| -
|
| - void Free(FreeSpace* node, int size_in_bytes);
|
| -
|
| - // Pick a node from the list.
|
| - FreeSpace* PickNodeFromList(int* node_size);
|
| -
|
| - // Pick a node from the list and compare it against {size_in_bytes}. If the
|
| - // node's size is greater or equal return the node and null otherwise.
|
| - FreeSpace* PickNodeFromList(int size_in_bytes, int* node_size);
|
| -
|
| - // Search for a node of size {size_in_bytes}.
|
| - FreeSpace* SearchForNodeInList(int size_in_bytes, int* node_size);
|
| -
|
| - intptr_t EvictFreeListItemsInList(Page* p);
|
| - bool ContainsPageFreeListItemsInList(Page* p);
|
| -
|
| - void RepairFreeList(Heap* heap);
|
| -
|
| - bool IsEmpty() { return top() == nullptr; }
|
| -
|
| - FreeList* owner() { return owner_; }
|
| - int available() const { return available_; }
|
| -
|
| -#ifdef DEBUG
|
| - intptr_t SumFreeList();
|
| - int FreeListLength();
|
| - bool IsVeryLong();
|
| -#endif
|
| -
|
| - private:
|
| - // For debug builds we accurately compute free lists lengths up until
|
| - // {kVeryLongFreeList} by manually walking the list.
|
| - static const int kVeryLongFreeList = 500;
|
| -
|
| - FreeSpace* top() { return top_.Value(); }
|
| - void set_top(FreeSpace* top) { top_.SetValue(top); }
|
| -
|
| - FreeSpace* end() const { return end_; }
|
| - void set_end(FreeSpace* end) { end_ = end; }
|
| -
|
| - // |type_|: The type of this free list category.
|
| - FreeListCategoryType type_;
|
| -
|
| - // |top_|: Points to the top FreeSpace* in the free list category.
|
| - AtomicValue<FreeSpace*> top_;
|
| -
|
| - // |end_|: Points to the end FreeSpace* in the free list category.
|
| - FreeSpace* end_;
|
| -
|
| - // |available_|: Total available bytes in all blocks of this free list
|
| - // category.
|
| - int available_;
|
| -
|
| - // |owner_|: The owning free list of this category.
|
| - FreeList* owner_;
|
| -};
|
| -
|
| // A free list maintaining free blocks of memory. The free list is organized in
|
| // a way to encourage objects allocated around the same time to be near each
|
| // other. The normal way to allocate is intended to be by bumping a 'top'
|
| @@ -1670,19 +1694,13 @@ class FreeList {
|
|
|
| explicit FreeList(PagedSpace* owner);
|
|
|
| - // The method concatenates {other} into {this} and returns the added bytes,
|
| - // including waste.
|
| - //
|
| - // Note: Thread-safe.
|
| - intptr_t Concatenate(FreeList* other);
|
| -
|
| // Adds a node on the free list. The block of size {size_in_bytes} starting
|
| // at {start} is placed on the free list. The return value is the number of
|
| // bytes that were not added to the free list, because they freed memory block
|
| // was too small. Bookkeeping information will be written to the block, i.e.,
|
| // its contents will be destroyed. The start address should be word aligned,
|
| // and the size should be a non-zero multiple of the word size.
|
| - int Free(Address start, int size_in_bytes);
|
| + int Free(Address start, int size_in_bytes, FreeMode mode);
|
|
|
| // Allocate a block of size {size_in_bytes} from the free list. The block is
|
| // unitialized. A failure is returned if no block is available. The size
|
| @@ -1692,49 +1710,83 @@ class FreeList {
|
| // Clear the free list.
|
| void Reset();
|
|
|
| - void ResetStats() { wasted_bytes_ = 0; }
|
| + void ResetStats() {
|
| + wasted_bytes_.SetValue(0);
|
| + ForAllFreeListCategories(
|
| + [](FreeListCategory* category) { category->ResetStats(); });
|
| + }
|
|
|
| // Return the number of bytes available on the free list.
|
| intptr_t Available() {
|
| intptr_t available = 0;
|
| - for (int i = kFirstCategory; i < kNumberOfCategories; i++) {
|
| - available += category_[i].available();
|
| - }
|
| + ForAllFreeListCategories([&available](FreeListCategory* category) {
|
| + available += category->available();
|
| + });
|
| return available;
|
| }
|
|
|
| - // The method tries to find a {FreeSpace} node of at least {size_in_bytes}
|
| - // size in the free list category exactly matching the size. If no suitable
|
| - // node could be found, the method falls back to retrieving a {FreeSpace}
|
| - // from the large or huge free list category.
|
| - //
|
| - // Can be used concurrently.
|
| - MUST_USE_RESULT FreeSpace* TryRemoveMemory(intptr_t hint_size_in_bytes);
|
| -
|
| bool IsEmpty() {
|
| - for (int i = kFirstCategory; i < kNumberOfCategories; i++) {
|
| - if (!category_[i].IsEmpty()) return false;
|
| - }
|
| - return true;
|
| + bool empty = true;
|
| + ForAllFreeListCategories([&empty](FreeListCategory* category) {
|
| + if (!category->is_empty()) empty = false;
|
| + });
|
| + return empty;
|
| }
|
|
|
| // Used after booting the VM.
|
| void RepairLists(Heap* heap);
|
|
|
| - intptr_t EvictFreeListItems(Page* p);
|
| - bool ContainsPageFreeListItems(Page* p);
|
| + intptr_t EvictFreeListItems(Page* page);
|
| + bool ContainsPageFreeListItems(Page* page);
|
|
|
| PagedSpace* owner() { return owner_; }
|
| - intptr_t wasted_bytes() { return wasted_bytes_; }
|
| - base::Mutex* mutex() { return &mutex_; }
|
| + intptr_t wasted_bytes() { return wasted_bytes_.Value(); }
|
| +
|
| + template <typename Callback>
|
| + void ForAllFreeListCategories(FreeListCategoryType type, Callback callback) {
|
| + FreeListCategory* current = categories_[type];
|
| + while (current != nullptr) {
|
| + FreeListCategory* next = current->next();
|
| + callback(current);
|
| + current = next;
|
| + }
|
| + }
|
| +
|
| + template <typename Callback>
|
| + void ForAllFreeListCategories(Callback callback) {
|
| + for (int i = kFirstCategory; i < kNumberOfCategories; i++) {
|
| + ForAllFreeListCategories(static_cast<FreeListCategoryType>(i), callback);
|
| + }
|
| + }
|
| +
|
| + bool AddCategory(FreeListCategory* category);
|
| + void RemoveCategory(FreeListCategory* category);
|
| + void PrintCategories(FreeListCategoryType type);
|
|
|
| #ifdef DEBUG
|
| - void Zap();
|
| intptr_t SumFreeLists();
|
| bool IsVeryLong();
|
| #endif
|
|
|
| private:
|
| + class FreeListCategoryIterator {
|
| + public:
|
| + FreeListCategoryIterator(FreeList* free_list, FreeListCategoryType type)
|
| + : current_(free_list->categories_[type]) {}
|
| +
|
| + bool HasNext() { return current_ != nullptr; }
|
| +
|
| + FreeListCategory* Next() {
|
| + DCHECK(HasNext());
|
| + FreeListCategory* tmp = current_;
|
| + current_ = current_->next();
|
| + return tmp;
|
| + }
|
| +
|
| + private:
|
| + FreeListCategory* current_;
|
| + };
|
| +
|
| // The size range of blocks, in bytes.
|
| static const int kMinBlockSize = 3 * kPointerSize;
|
| static const int kMaxBlockSize = Page::kAllocatableMemory;
|
| @@ -1750,11 +1802,19 @@ class FreeList {
|
| static const int kLargeAllocationMax = kMediumListMax;
|
|
|
| FreeSpace* FindNodeFor(int size_in_bytes, int* node_size);
|
| - FreeSpace* FindNodeIn(FreeListCategoryType category, int* node_size);
|
|
|
| - FreeListCategory* GetFreeListCategory(FreeListCategoryType category) {
|
| - return &category_[category];
|
| - }
|
| + // Walks all available categories for a given |type| and tries to retrieve
|
| + // a node. Returns nullptr if the category is empty.
|
| + FreeSpace* FindNodeIn(FreeListCategoryType type, int* node_size);
|
| +
|
| + // Tries to retrieve a node from the first category in a given |type|.
|
| + // Returns nullptr if the category is empty.
|
| + FreeSpace* TryFindNodeIn(FreeListCategoryType type, int* node_size,
|
| + int minimum_size);
|
| +
|
| + // Searches a given |type| for a node of at least |minimum_size|.
|
| + FreeSpace* SearchForNodeInList(FreeListCategoryType type, int* node_size,
|
| + int minimum_size);
|
|
|
| FreeListCategoryType SelectFreeListCategoryType(size_t size_in_bytes) {
|
| if (size_in_bytes <= kTiniestListMax) {
|
| @@ -1784,10 +1844,13 @@ class FreeList {
|
| return kHuge;
|
| }
|
|
|
| + FreeListCategory* top(FreeListCategoryType type) { return categories_[type]; }
|
| +
|
| PagedSpace* owner_;
|
| - base::Mutex mutex_;
|
| - intptr_t wasted_bytes_;
|
| - FreeListCategory category_[kNumberOfCategories];
|
| + AtomicNumber<intptr_t> wasted_bytes_;
|
| + FreeListCategory* categories_[kNumberOfCategories];
|
| +
|
| + friend class FreeListCategory;
|
|
|
| DISALLOW_IMPLICIT_CONSTRUCTORS(FreeList);
|
| };
|
| @@ -1889,7 +1952,6 @@ class LocalAllocationBuffer {
|
| AllocationInfo allocation_info_;
|
| };
|
|
|
| -
|
| class PagedSpace : public Space {
|
| public:
|
| static const intptr_t kCompactionMemoryWanted = 500 * KB;
|
| @@ -1946,11 +2008,6 @@ class PagedSpace : public Space {
|
| ResetFreeListStatistics();
|
| }
|
|
|
| - // Increases the number of available bytes of that space.
|
| - void AddToAccountingStats(intptr_t bytes) {
|
| - accounting_stats_.DeallocateBytes(bytes);
|
| - }
|
| -
|
| // Available bytes without growing. These are the bytes on the free list.
|
| // The bytes in the linear allocation area are not included in this total
|
| // because updating the stats would slow down allocation. New pages are
|
| @@ -2006,11 +2063,16 @@ class PagedSpace : public Space {
|
| // If add_to_freelist is false then just accounting stats are updated and
|
| // no attempt to add area to free list is made.
|
| int Free(Address start, int size_in_bytes) {
|
| - int wasted = free_list_.Free(start, size_in_bytes);
|
| + int wasted = free_list_.Free(start, size_in_bytes, kLinkCategory);
|
| accounting_stats_.DeallocateBytes(size_in_bytes);
|
| return size_in_bytes - wasted;
|
| }
|
|
|
| + int UnaccountedFree(Address start, int size_in_bytes) {
|
| + int wasted = free_list_.Free(start, size_in_bytes, kDoNotLinkCategory);
|
| + return size_in_bytes - wasted;
|
| + }
|
| +
|
| void ResetFreeList() { free_list_.Reset(); }
|
|
|
| // Set space allocation info.
|
| @@ -2035,7 +2097,7 @@ class PagedSpace : public Space {
|
| void IncreaseCapacity(int size);
|
|
|
| // Releases an unused page and shrinks the space.
|
| - void ReleasePage(Page* page, bool evict_free_list_items);
|
| + void ReleasePage(Page* page);
|
|
|
| // The dummy page that anchors the linked list of pages.
|
| Page* anchor() { return &anchor_; }
|
| @@ -2091,17 +2153,18 @@ class PagedSpace : public Space {
|
| // sweeper.
|
| virtual void RefillFreeList();
|
|
|
| - protected:
|
| - void AddMemory(Address start, intptr_t size);
|
| + FreeList* free_list() { return &free_list_; }
|
|
|
| - void MoveOverFreeMemory(PagedSpace* other);
|
| + base::Mutex* mutex() { return &space_mutex_; }
|
|
|
| + inline void UnlinkFreeListCategories(Page* page);
|
| + inline intptr_t RelinkFreeListCategories(Page* page);
|
| +
|
| + protected:
|
| // PagedSpaces that should be included in snapshots have different, i.e.,
|
| // smaller, initial pages.
|
| virtual bool snapshotable() { return true; }
|
|
|
| - FreeList* free_list() { return &free_list_; }
|
| -
|
| bool HasPages() { return anchor_.next_page() != &anchor_; }
|
|
|
| // Cleans up the space, frees all pages in this space except those belonging
|
| @@ -2799,8 +2862,6 @@ class CompactionSpace : public PagedSpace {
|
|
|
| bool is_local() override { return true; }
|
|
|
| - void RefillFreeList() override;
|
| -
|
| protected:
|
| // The space is temporary and not included in any snapshots.
|
| bool snapshotable() override { return false; }
|
|
|