v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
free-list.h
Go to the documentation of this file.
1// Copyright 2020 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_HEAP_FREE_LIST_H_
6#define V8_HEAP_FREE_LIST_H_
7
8#include <atomic>
9
10#include "src/base/macros.h"
11#include "src/common/globals.h"
15#include "src/objects/map.h"
16#include "src/utils/utils.h"
17#include "testing/gtest/include/gtest/gtest_prod.h" // nogncheck
18
19namespace v8 {
20namespace internal {
21
22namespace heap {
23class HeapTester;
24class TestCodePageAllocatorScope;
25} // namespace heap
26
27class AllocationObserver;
28class FreeList;
29class Isolate;
30class LargeObjectSpace;
31class LargePageMetadata;
32class LinearAllocationArea;
33class PageMetadata;
34class PagedSpace;
35class SemiSpace;
36
37using FreeListCategoryType = int32_t;
38
41
43
44// A free list category maintains a linked list of free memory blocks.
46 public:
48 type_ = type;
49 available_ = 0;
50 prev_ = nullptr;
51 next_ = nullptr;
52 }
53
54 // Unlinks the category from the freelist.
55 void Unlink(FreeList* owner);
56 // Resets all the fields of the category.
57 void Reset(FreeList* owner);
58
60
61 // Relinks the category into the currently owning free list. Requires that the
62 // category is currently unlinked.
63 void Relink(FreeList* owner);
64
65 void Free(const WritableFreeSpace& writable_free_space, FreeMode mode,
66 FreeList* owner);
67
68 // Performs a single try to pick a node of at least |minimum_size| from the
69 // category. Stores the actual size in |node_size|. Returns nullptr if no
70 // node is found.
72 size_t* node_size);
73
74 // Picks a node of at least |minimum_size| from the category. Stores the
75 // actual size in |node_size|. Returns nullptr if no node is found.
76 Tagged<FreeSpace> SearchForNodeInList(size_t minimum_size, size_t* node_size);
77
78 inline bool is_linked(FreeList* owner) const;
79 bool is_empty() { return top().is_null(); }
80 uint32_t available() const { return available_; }
81
82 size_t SumFreeList();
83 int FreeListLength();
84
85 template <typename Callback>
87 for (Tagged<FreeSpace> cur_node = top(); !cur_node.is_null();
88 cur_node = cur_node->next()) {
89 callback(cur_node);
90 }
91 }
92
93 private:
94 // For debug builds we accurately compute free lists lengths up until
95 // {kVeryLongFreeList} by manually walking the list.
96 static constexpr int kVeryLongFreeList = 500;
97
98 // Updates |available_|, |length_| and free_list_->Available() after an
99 // allocation of size |allocation_size|.
100 inline void UpdateCountersAfterAllocation(size_t allocation_size);
101
108
109 // |type_|: The type of this free list category.
111
112 // |available_|: Total available bytes in all blocks of this free list
113 // category.
114 uint32_t available_ = 0;
115
116 // |top_|: Points to the top FreeSpace in the free list category.
118
121
122 friend class FreeList;
123 friend class FreeListManyCached;
124 friend class PagedSpace;
125 friend class MapSpace;
126};
127
128// A free list maintains free blocks of memory. The free list is organized in
129// a way to encourage objects allocated around the same time to be near each
130// other. The normal way to allocate is intended to be by bumping a 'top'
131// pointer until it hits a 'limit' pointer. When the limit is hit we need to
132// find a new space to allocate from. This is done with the free list, which is
133// divided up into rough categories to cut down on waste. Having finer
134// categories would scatter allocation more.
135class FreeList {
136 public:
137 // Creates a Freelist of the default class.
138 V8_EXPORT_PRIVATE static std::unique_ptr<FreeList> CreateFreeList();
139 // Creates a Freelist for new space.
140 V8_EXPORT_PRIVATE static std::unique_ptr<FreeList>
142
144 virtual ~FreeList() = default;
145
146 // Adds a node on the free list. The block of size {size_in_bytes} starting
147 // at {start} is placed on the free list. The return value is the number of
148 // bytes that were not added to the free list, because the freed memory block
149 // was too small. Bookkeeping information will be written to the block, i.e.,
150 // its contents will be destroyed. The start address should be word aligned,
151 // and the size should be a non-zero multiple of the word size.
152 virtual size_t Free(const WritableFreeSpace& free_space, FreeMode mode);
153
154 // Allocates a free space node from the free list of at least size_in_bytes
155 // bytes. Returns the actual node size in node_size which can be bigger than
156 // size_in_bytes. This method returns null if the allocation request cannot be
157 // handled by the free list.
159 size_t size_in_bytes, size_t* node_size, AllocationOrigin origin) = 0;
160
161 // Returns a page containing an entry for a given type, or nullptr otherwise.
163 size_t size_in_bytes) = 0;
164
165 virtual void Reset();
166 virtual void ResetForNonBlackAllocatedPages();
167
168 // Return the number of bytes available on the free list.
169 size_t Available() {
171 return available_;
172 }
173
174 // Update number of available bytes on the Freelists.
175 void IncreaseAvailableBytes(size_t bytes) { available_ += bytes; }
176 void DecreaseAvailableBytes(size_t bytes) { available_ -= bytes; }
177
178 size_t wasted_bytes() const {
179 return wasted_bytes_.load(std::memory_order_relaxed);
180 }
181 void increase_wasted_bytes(size_t bytes) {
182 wasted_bytes_.fetch_add(bytes, std::memory_order_relaxed);
183 }
184 void decrease_wasted_bytes(size_t bytes) {
185 wasted_bytes_.fetch_sub(bytes, std::memory_order_relaxed);
186 }
187
188 inline bool IsEmpty();
189
190 // Used after booting the VM.
191 void RepairLists(Heap* heap);
192
194
197
198 size_t min_block_size() const { return min_block_size_; }
199
200 template <typename Callback>
203 while (current != nullptr) {
204 FreeListCategory* next = current->next();
205 callback(current);
206 current = next;
207 }
208 }
209
210 template <typename Callback>
212 for (int i = kFirstCategory; i < number_of_categories(); i++) {
214 }
215 }
216
217 virtual bool AddCategory(FreeListCategory* category);
218 virtual V8_EXPORT_PRIVATE void RemoveCategory(FreeListCategory* category);
220
221 protected:
223 public:
225 : current_(free_list->categories_[type]) {}
226
227 bool HasNext() const { return current_ != nullptr; }
228
230 DCHECK(HasNext());
233 return tmp;
234 }
235
236 private:
238 };
239
240#ifdef DEBUG
241 V8_EXPORT_PRIVATE size_t SumFreeLists();
242 V8_EXPORT_PRIVATE bool IsVeryLong();
243#endif
244
246 DCHECK(IsVeryLong() || available_ == SumFreeLists());
247 }
248
249 // Tries to retrieve a node from the first category in a given |type|.
250 // Returns nullptr if the category is empty or the top entry is smaller
251 // than minimum_size.
253 size_t minimum_size, size_t* node_size);
254
255 // Searches a given |type| for a node of at least |minimum_size|.
257 size_t minimum_size, size_t* node_size);
258
259 // Returns the smallest category in which an object of |size_in_bytes| could
260 // fit.
262 size_t size_in_bytes) = 0;
263
265 return categories_[type];
266 }
267
269
272 size_t min_block_size_ = 0;
273
275
276 // The number of bytes in this freelist that are available for allocation.
277 size_t available_ = 0;
278 // Number of wasted bytes in this free list that are not available for
279 // allocation.
280 std::atomic<size_t> wasted_bytes_ = 0;
281
282 friend class FreeListCategory;
283 friend class PageMetadata;
286 friend class MapSpace;
287};
288
289// Use 24 Freelists: on per 16 bytes between 24 and 256, and then a few ones for
290// larger sizes. See the variable |categories_min| for the size of each
291// Freelist. Allocation is done using a best-fit strategy (considering only the
292// first element of each category though).
293// Performances are expected to be worst than FreeListLegacy, but memory
294// consumption should be lower (since fragmentation should be lower).
296 public:
297 PageMetadata* GetPageForSize(size_t size_in_bytes) override;
298
299 FreeListMany();
300 ~FreeListMany() override;
301
303 size_t size_in_bytes, size_t* node_size,
304 AllocationOrigin origin) override;
305
306 protected:
307 static constexpr size_t kMinBlockSize = 3 * kTaggedSize;
308
309 // This is a conservative upper bound. The actual maximum block size takes
310 // padding and alignment of data and code pages into account.
311 static constexpr size_t kMaxBlockSize = MutablePageMetadata::kPageSize;
312 // Largest size for which categories are still precise, and for which we can
313 // therefore compute the category in constant time.
314 static constexpr size_t kPreciseCategoryMaxSize = 256;
315
316 // Categories boundaries generated with:
317 // perl -E '
318 // @cat = (24, map {$_*16} 2..16, 48, 64);
319 // while ($cat[-1] <= 32768) {
320 // push @cat, $cat[-1]*2
321 // }
322 // say join ", ", @cat;
323 // say "\n", scalar @cat'
324 static constexpr int kNumberOfCategories = 24;
325 static constexpr unsigned int categories_min[kNumberOfCategories] = {
326 24, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192,
327 208, 224, 240, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536};
328
329 // Return the smallest category that could hold |size_in_bytes| bytes.
331 size_t size_in_bytes) override {
332 if (size_in_bytes <= kPreciseCategoryMaxSize) {
333 if (size_in_bytes < categories_min[1]) return 0;
334 return static_cast<FreeListCategoryType>(size_in_bytes >> 4) - 1;
335 }
336 for (int cat = (kPreciseCategoryMaxSize >> 4) - 1; cat < last_category_;
337 cat++) {
338 if (size_in_bytes < categories_min[cat + 1]) {
339 return cat;
340 }
341 }
342 return last_category_;
343 }
344
345 FRIEND_TEST(SpacesTest, FreeListManySelectFreeListCategoryType);
346 FRIEND_TEST(SpacesTest, FreeListManyGuaranteedAllocatable);
347};
348
349// Same as FreeListMany but uses a cache to know which categories are empty.
350// The cache (|next_nonempty_category|) is maintained in a way such that for
351// each category c, next_nonempty_category[c] contains the first non-empty
352// category greater or equal to c, that may hold an object of size c.
353// Allocation is done using the same strategy as FreeListMany (ie, best fit).
355 public:
357
359 size_t size_in_bytes, size_t* node_size,
360 AllocationOrigin origin) override;
361
362 size_t Free(const WritableFreeSpace& free_space, FreeMode mode) override;
363
364 void Reset() override;
365 void ResetForNonBlackAllocatedPages() override;
366
367 bool AddCategory(FreeListCategory* category) override;
368 void RemoveCategory(FreeListCategory* category) override;
369
370 protected:
371 // Updates the cache after adding something in the category |cat|.
373 for (int i = cat; i >= kFirstCategory && next_nonempty_category[i] > cat;
374 i--) {
375 next_nonempty_category[i] = cat;
376 }
377 }
378
379 // Updates the cache after emptying category |cat|.
381 for (int i = cat; i >= kFirstCategory && next_nonempty_category[i] == cat;
382 i--) {
383 next_nonempty_category[i] = next_nonempty_category[cat + 1];
384 }
385 }
386
387#ifdef DEBUG
388 void CheckCacheIntegrity() {
389 for (int i = 0; i <= last_category_; i++) {
390 DCHECK(next_nonempty_category[i] == last_category_ + 1 ||
391 categories_[next_nonempty_category[i]] != nullptr);
392 for (int j = i; j < next_nonempty_category[i]; j++) {
393 DCHECK(categories_[j] == nullptr);
394 }
395 }
396 }
397#endif
398
399 // The cache is overallocated by one so that the last element is always
400 // defined, and when updating the cache, we can always use cache[i+1] as long
401 // as i is < kNumberOfCategories.
402 int next_nonempty_category[kNumberOfCategories + 1];
403
404 private:
405 void ResetCache() {
406 for (int i = 0; i < kNumberOfCategories; i++) {
407 next_nonempty_category[i] = kNumberOfCategories;
408 }
409 // Setting the after-last element as well, as explained in the cache's
410 // declaration.
411 next_nonempty_category[kNumberOfCategories] = kNumberOfCategories;
412 }
413};
414
415// Same as FreeListManyCached but uses a fast path.
416// The fast path overallocates by at least 1.85k bytes. The idea of this 1.85k
417// is: we want the fast path to always overallocate, even for larger
418// categories. Therefore, we have two choices: either overallocate by
419// "size_in_bytes * something" or overallocate by "size_in_bytes +
420// something". We choose the later, as the former will tend to overallocate too
421// much for larger objects. The 1.85k (= 2048 - 128) has been chosen such that
422// for tiny objects (size <= 128 bytes), the first category considered is the
423// 36th (which holds objects of 2k to 3k), while for larger objects, the first
424// category considered will be one that guarantees a 1.85k+ bytes
425// overallocation. Using 2k rather than 1.85k would have resulted in either a
426// more complex logic for SelectFastAllocationFreeListCategoryType, or the 36th
427// category (2k to 3k) not being used; both of which are undesirable.
428// A secondary fast path is used for tiny objects (size <= 128), in order to
429// consider categories from 256 to 2048 bytes for them.
430// Note that this class uses a precise GetPageForSize (inherited from
431// FreeListMany), which makes its fast path less fast in the Scavenger. This is
432// done on purpose, since this class's only purpose is to be used by
433// FreeListManyCachedOrigin, which is precise for the scavenger.
435 : public FreeListManyCached {
436 public:
437 enum class SmallBlocksMode { kAllow, kProhibit };
438
440 : small_blocks_mode_(small_blocks_mode) {
441 if (small_blocks_mode_ == SmallBlocksMode::kProhibit) {
442 min_block_size_ =
443 (v8_flags.minor_ms && (v8_flags.minor_ms_min_lab_size_kb > 0))
444 ? (v8_flags.minor_ms_min_lab_size_kb * KB)
445 : kFastPathStart;
446 }
447 }
448
450 size_t size_in_bytes, size_t* node_size,
451 AllocationOrigin origin) override;
452
453 protected:
454 // Objects in the 18th category are at least 2048 bytes
455 static const FreeListCategoryType kFastPathFirstCategory = 18;
456 static const size_t kFastPathStart = 2048;
457 static const size_t kTinyObjectMaxSize = 128;
458 static const size_t kFastPathOffset = kFastPathStart - kTinyObjectMaxSize;
459 // Objects in the 15th category are at least 256 bytes
460 static const FreeListCategoryType kFastPathFallBackTiny = 15;
461
462 static_assert(categories_min[kFastPathFirstCategory] == kFastPathStart);
463 static_assert(categories_min[kFastPathFallBackTiny] ==
464 kTinyObjectMaxSize * 2);
465
467 size_t size_in_bytes) {
468 DCHECK(size_in_bytes < kMaxBlockSize);
469
470 if (size_in_bytes >= categories_min[last_category_]) return last_category_;
471
472 size_in_bytes += kFastPathOffset;
473 for (int cat = kFastPathFirstCategory; cat < last_category_; cat++) {
474 if (size_in_bytes <= categories_min[cat]) {
475 return cat;
476 }
477 }
478 return last_category_;
479 }
480
481 private:
483
485 SpacesTest,
486 FreeListManyCachedFastPathSelectFastAllocationFreeListCategoryType);
487};
488
494
501
502// Uses FreeListManyCached if in the GC; FreeListManyCachedFastPath otherwise.
503// The reasoning behind this FreeList is the following: the GC runs in
504// parallel, and therefore, more expensive allocations there are less
505// noticeable. On the other hand, the generated code and runtime need to be very
506// fast. Therefore, the strategy for the former is one that is not very
507// efficient, but reduces fragmentation (FreeListManyCached), while the strategy
508// for the later is one that is very efficient, but introduces some
509// fragmentation (FreeListManyCachedFastPath).
512 public:
514 size_t size_in_bytes, size_t* node_size,
515 AllocationOrigin origin) override;
516};
517
518} // namespace internal
519} // namespace v8
520
521#endif // V8_HEAP_FREE_LIST_H_
void Relink(FreeList *owner)
Definition free-list.cc:118
uint32_t available() const
Definition free-list.h:80
Tagged< FreeSpace > top()
Definition free-list.h:102
FreeListCategory * prev_
Definition free-list.h:119
bool is_linked(FreeList *owner) const
FreeListCategory * next()
Definition free-list.h:106
void RepairFreeList(Heap *heap)
Definition free-list.cc:104
static constexpr int kVeryLongFreeList
Definition free-list.h:96
void Initialize(FreeListCategoryType type)
Definition free-list.h:47
FreeListCategory * next_
Definition free-list.h:120
void Reset(FreeList *owner)
Definition free-list.cc:29
FreeListCategory * prev()
Definition free-list.h:104
FreeListCategoryType type_
Definition free-list.h:110
void Free(const WritableFreeSpace &writable_free_space, FreeMode mode, FreeList *owner)
Definition free-list.cc:86
Tagged< FreeSpace > SearchForNodeInList(size_t minimum_size, size_t *node_size)
Definition free-list.cc:50
void UpdateCountersAfterAllocation(size_t allocation_size)
void set_prev(FreeListCategory *prev)
Definition free-list.h:105
V8_EXPORT_PRIVATE Tagged< FreeSpace > PickNodeFromList(size_t minimum_size, size_t *node_size)
Definition free-list.cc:35
void set_top(Tagged< FreeSpace > top)
Definition free-list.h:103
void set_next(FreeListCategory *next)
Definition free-list.h:107
void Unlink(FreeList *owner)
Definition free-list.cc:21
void IterateNodesForTesting(Callback callback)
Definition free-list.h:86
Tagged< FreeSpace > top_
Definition free-list.h:117
FRIEND_TEST(SpacesTest, FreeListManyCachedFastPathSelectFastAllocationFreeListCategoryType)
FreeListCategoryType SelectFastAllocationFreeListCategoryType(size_t size_in_bytes)
Definition free-list.h:466
FreeListManyCachedFastPathBase(SmallBlocksMode small_blocks_mode)
Definition free-list.h:439
void UpdateCacheAfterAddition(FreeListCategoryType cat)
Definition free-list.h:372
void UpdateCacheAfterRemoval(FreeListCategoryType cat)
Definition free-list.h:380
FreeListCategoryType SelectFreeListCategoryType(size_t size_in_bytes) override
Definition free-list.h:330
FRIEND_TEST(SpacesTest, FreeListManySelectFreeListCategoryType)
FRIEND_TEST(SpacesTest, FreeListManyGuaranteedAllocatable)
FreeListCategoryIterator(FreeList *free_list, FreeListCategoryType type)
Definition free-list.h:224
size_t wasted_bytes() const
Definition free-list.h:178
virtual FreeListCategoryType SelectFreeListCategoryType(size_t size_in_bytes)=0
virtual void ResetForNonBlackAllocatedPages()
Definition free-list.cc:452
static V8_EXPORT_PRIVATE std::unique_ptr< FreeList > CreateFreeList()
Definition free-list.cc:131
FreeList(int number_of_categories, size_t min_block_size)
Definition free-list.cc:126
const int number_of_categories_
Definition free-list.h:270
void ForAllFreeListCategories(Callback callback)
Definition free-list.h:211
void increase_wasted_bytes(size_t bytes)
Definition free-list.h:181
friend class MapSpace
Definition free-list.h:286
Tagged< FreeSpace > SearchForNodeInList(FreeListCategoryType type, size_t minimum_size, size_t *node_size)
Definition free-list.cc:155
V8_EXPORT_PRIVATE void EvictFreeListItems(PageMetadata *page)
Definition free-list.cc:471
virtual bool AddCategory(FreeListCategory *category)
Definition free-list.cc:486
FreeListCategory ** categories_
Definition free-list.h:274
void IncreaseAvailableBytes(size_t bytes)
Definition free-list.h:175
std::atomic< size_t > wasted_bytes_
Definition free-list.h:280
virtual ~FreeList()=default
PageMetadata * GetPageForCategoryType(FreeListCategoryType type)
void ForAllFreeListCategories(FreeListCategoryType type, Callback callback)
Definition free-list.h:201
static V8_EXPORT_PRIVATE std::unique_ptr< FreeList > CreateFreeListForNewSpace()
Definition free-list.cc:135
FreeListCategoryType last_category()
Definition free-list.h:196
virtual size_t Free(const WritableFreeSpace &free_space, FreeMode mode)
Definition free-list.cc:175
void DecreaseAvailableBytes(size_t bytes)
Definition free-list.h:176
void decrease_wasted_bytes(size_t bytes)
Definition free-list.h:184
virtual void Reset()
Definition free-list.cc:442
const FreeListCategoryType last_category_
Definition free-list.h:271
size_t min_block_size() const
Definition free-list.h:198
virtual V8_WARN_UNUSED_RESULT Tagged< FreeSpace > Allocate(size_t size_in_bytes, size_t *node_size, AllocationOrigin origin)=0
FreeListCategory * top(FreeListCategoryType type) const
Definition free-list.h:264
virtual V8_EXPORT_PRIVATE void RemoveCategory(FreeListCategory *category)
Definition free-list.cc:505
void RepairLists(Heap *heap)
Definition free-list.cc:481
virtual V8_EXPORT_PRIVATE PageMetadata * GetPageForSize(size_t size_in_bytes)=0
void PrintCategories(FreeListCategoryType type)
Definition free-list.cc:528
Tagged< FreeSpace > TryFindNodeIn(FreeListCategoryType type, size_t minimum_size, size_t *node_size)
Definition free-list.cc:139
V8_INLINE constexpr bool is_null() const
Definition tagged.h:502
TNode< Object > callback
Register tmp
static constexpr FreeListCategoryType kFirstCategory
Definition free-list.h:39
constexpr int kTaggedSize
Definition globals.h:542
int32_t FreeListCategoryType
Definition free-list.h:37
@ kDoNotLinkCategory
Definition free-list.h:42
V8_EXPORT_PRIVATE FlagValues v8_flags
static constexpr FreeListCategoryType kInvalidCategory
Definition free-list.h:40
#define DCHECK(condition)
Definition logging.h:482
#define V8_EXPORT_PRIVATE
Definition macros.h:460
#define V8_WARN_UNUSED_RESULT
Definition v8config.h:671
wasm::ValueType type