Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(412)

Unified Diff: src/heap/spaces.h

Issue 1772733002: [heap] Move to two-level free-list (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Rebase on disabled black allocation Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/heap/mark-compact.cc ('k') | src/heap/spaces.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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; }
« no previous file with comments | « src/heap/mark-compact.cc ('k') | src/heap/spaces.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698