• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_NEW_SPACES_H_
6 #define V8_HEAP_NEW_SPACES_H_
7 
8 #include <atomic>
9 #include <memory>
10 
11 #include "src/base/macros.h"
12 #include "src/base/platform/mutex.h"
13 #include "src/common/globals.h"
14 #include "src/heap/heap.h"
15 #include "src/heap/spaces.h"
16 #include "src/logging/log.h"
17 #include "src/objects/heap-object.h"
18 
19 namespace v8 {
20 namespace internal {
21 
22 class Heap;
23 class MemoryChunk;
24 
25 enum SemiSpaceId { kFromSpace = 0, kToSpace = 1 };
26 
27 using ParkedAllocationBuffer = std::pair<int, Address>;
28 using ParkedAllocationBuffersVector = std::vector<ParkedAllocationBuffer>;
29 
30 // -----------------------------------------------------------------------------
31 // SemiSpace in young generation
32 //
33 // A SemiSpace is a contiguous chunk of memory holding page-like memory chunks.
34 // The mark-compact collector  uses the memory of the first page in the from
35 // space as a marking stack when tracing live objects.
36 class SemiSpace : public Space {
37  public:
38   using iterator = PageIterator;
39   using const_iterator = ConstPageIterator;
40 
41   static void Swap(SemiSpace* from, SemiSpace* to);
42 
SemiSpace(Heap * heap,SemiSpaceId semispace)43   SemiSpace(Heap* heap, SemiSpaceId semispace)
44       : Space(heap, NEW_SPACE, new NoFreeList()),
45         current_capacity_(0),
46         target_capacity_(0),
47         maximum_capacity_(0),
48         minimum_capacity_(0),
49         age_mark_(kNullAddress),
50         id_(semispace),
51         current_page_(nullptr) {}
52 
53   inline bool Contains(HeapObject o) const;
54   inline bool Contains(Object o) const;
55   inline bool ContainsSlow(Address a) const;
56 
57   void SetUp(size_t initial_capacity, size_t maximum_capacity);
58   void TearDown();
59 
60   bool Commit();
61   bool Uncommit();
IsCommitted()62   bool IsCommitted() const { return !memory_chunk_list_.Empty(); }
63 
64   // Grow the semispace to the new capacity.  The new capacity requested must
65   // be larger than the current capacity and less than the maximum capacity.
66   bool GrowTo(size_t new_capacity);
67 
68   // Shrinks the semispace to the new capacity.  The new capacity requested
69   // must be more than the amount of used memory in the semispace and less
70   // than the current capacity.
71   void ShrinkTo(size_t new_capacity);
72 
73   bool EnsureCurrentCapacity();
74 
75   // Returns the start address of the first page of the space.
space_start()76   Address space_start() const {
77     DCHECK_NE(memory_chunk_list_.front(), nullptr);
78     return memory_chunk_list_.front()->area_start();
79   }
80 
current_page()81   Page* current_page() { return current_page_; }
82 
83   // Returns the start address of the current page of the space.
page_low()84   Address page_low() const { return current_page_->area_start(); }
85 
86   // Returns one past the end address of the current page of the space.
page_high()87   Address page_high() const { return current_page_->area_end(); }
88 
AdvancePage()89   bool AdvancePage() {
90     Page* next_page = current_page_->next_page();
91     // We cannot expand if we reached the target capcity. Note
92     // that we need to account for the next page already for this check as we
93     // could potentially fill the whole page after advancing.
94     if (next_page == nullptr || (current_capacity_ == target_capacity_)) {
95       return false;
96     }
97     current_page_ = next_page;
98     current_capacity_ += Page::kPageSize;
99     return true;
100   }
101 
102   // Resets the space to using the first page.
103   void Reset();
104 
105   void RemovePage(Page* page);
106   void PrependPage(Page* page);
107   void MovePageToTheEnd(Page* page);
108 
109   Page* InitializePage(MemoryChunk* chunk) override;
110 
111   // Age mark accessors.
age_mark()112   Address age_mark() const { return age_mark_; }
113   void set_age_mark(Address mark);
114 
115   // Returns the current capacity of the semispace.
current_capacity()116   size_t current_capacity() const { return current_capacity_; }
117 
118   // Returns the target capacity of the semispace.
target_capacity()119   size_t target_capacity() const { return target_capacity_; }
120 
121   // Returns the maximum capacity of the semispace.
maximum_capacity()122   size_t maximum_capacity() const { return maximum_capacity_; }
123 
124   // Returns the initial capacity of the semispace.
minimum_capacity()125   size_t minimum_capacity() const { return minimum_capacity_; }
126 
id()127   SemiSpaceId id() const { return id_; }
128 
129   // Approximate amount of physical memory committed for this space.
130   size_t CommittedPhysicalMemory() const override;
131 
132   // If we don't have these here then SemiSpace will be abstract.  However
133   // they should never be called:
134 
Size()135   size_t Size() const override { UNREACHABLE(); }
136 
SizeOfObjects()137   size_t SizeOfObjects() const override { return Size(); }
138 
Available()139   size_t Available() const override { UNREACHABLE(); }
140 
first_page()141   Page* first_page() override {
142     return reinterpret_cast<Page*>(memory_chunk_list_.front());
143   }
last_page()144   Page* last_page() override {
145     return reinterpret_cast<Page*>(memory_chunk_list_.back());
146   }
147 
first_page()148   const Page* first_page() const override {
149     return reinterpret_cast<const Page*>(memory_chunk_list_.front());
150   }
last_page()151   const Page* last_page() const override {
152     return reinterpret_cast<const Page*>(memory_chunk_list_.back());
153   }
154 
begin()155   iterator begin() { return iterator(first_page()); }
end()156   iterator end() { return iterator(nullptr); }
157 
begin()158   const_iterator begin() const { return const_iterator(first_page()); }
end()159   const_iterator end() const { return const_iterator(nullptr); }
160 
161   std::unique_ptr<ObjectIterator> GetObjectIterator(Heap* heap) override;
162 
163 #ifdef DEBUG
164   V8_EXPORT_PRIVATE void Print() override;
165   // Validate a range of of addresses in a SemiSpace.
166   // The "from" address must be on a page prior to the "to" address,
167   // in the linked page order, or it must be earlier on the same page.
168   static void AssertValidRange(Address from, Address to);
169 #else
170   // Do nothing.
AssertValidRange(Address from,Address to)171   inline static void AssertValidRange(Address from, Address to) {}
172 #endif
173 
174 #ifdef VERIFY_HEAP
175   virtual void Verify() const;
176 #endif
177 
178   void AddRangeToActiveSystemPages(Address start, Address end);
179 
180  private:
181   void RewindPages(int num_pages);
182 
183   // Copies the flags into the masked positions on all pages in the space.
184   void FixPagesFlags(Page::MainThreadFlags flags, Page::MainThreadFlags mask);
185 
186   void IncrementCommittedPhysicalMemory(size_t increment_value);
187   void DecrementCommittedPhysicalMemory(size_t decrement_value);
188 
189   // The currently committed space capacity.
190   size_t current_capacity_;
191 
192   // The targetted committed space capacity.
193   size_t target_capacity_;
194 
195   // The maximum capacity that can be used by this space. A space cannot grow
196   // beyond that size.
197   size_t maximum_capacity_;
198 
199   // The minimum capacity for the space. A space cannot shrink below this size.
200   size_t minimum_capacity_;
201 
202   // Used to govern object promotion during mark-compact collection.
203   Address age_mark_;
204 
205   size_t committed_physical_memory_{0};
206 
207   SemiSpaceId id_;
208 
209   Page* current_page_;
210 
211   friend class NewSpace;
212   friend class SemiSpaceObjectIterator;
213 };
214 
215 // A SemiSpaceObjectIterator is an ObjectIterator that iterates over the active
216 // semispace of the heap's new space.  It iterates over the objects in the
217 // semispace from a given start address (defaulting to the bottom of the
218 // semispace) to the top of the semispace.  New objects allocated after the
219 // iterator is created are not iterated.
220 class SemiSpaceObjectIterator : public ObjectIterator {
221  public:
222   // Create an iterator over the allocated objects in the given to-space.
223   explicit SemiSpaceObjectIterator(const NewSpace* space);
224 
225   inline HeapObject Next() override;
226 
227  private:
228   void Initialize(Address start, Address end);
229 
230   // The current iteration point.
231   Address current_;
232   // The end of iteration.
233   Address limit_;
234 };
235 
236 // -----------------------------------------------------------------------------
237 // The young generation space.
238 //
239 // The new space consists of a contiguous pair of semispaces.  It simply
240 // forwards most functions to the appropriate semispace.
241 
242 class V8_EXPORT_PRIVATE NewSpace final
NON_EXPORTED_BASE(public SpaceWithLinearArea)243     : NON_EXPORTED_BASE(public SpaceWithLinearArea) {
244  public:
245   using iterator = PageIterator;
246   using const_iterator = ConstPageIterator;
247 
248   NewSpace(Heap* heap, v8::PageAllocator* page_allocator,
249            size_t initial_semispace_capacity, size_t max_semispace_capacity,
250            LinearAllocationArea* allocation_info);
251 
252   ~NewSpace() override;
253 
254   inline bool ContainsSlow(Address a) const;
255   inline bool Contains(Object o) const;
256   inline bool Contains(HeapObject o) const;
257 
258   void ResetParkedAllocationBuffers();
259 
260   // Flip the pair of spaces.
261   void Flip();
262 
263   // Grow the capacity of the semispaces.  Assumes that they are not at
264   // their maximum capacity.
265   void Grow();
266 
267   // Shrink the capacity of the semispaces.
268   void Shrink();
269 
270   // Return the allocated bytes in the active semispace.
271   size_t Size() const final {
272     DCHECK_GE(top(), to_space_.page_low());
273     return (to_space_.current_capacity() - Page::kPageSize) / Page::kPageSize *
274                MemoryChunkLayout::AllocatableMemoryInDataPage() +
275            static_cast<size_t>(top() - to_space_.page_low());
276   }
277 
278   size_t SizeOfObjects() const final { return Size(); }
279 
280   // Return the allocatable capacity of a semispace.
281   size_t Capacity() const {
282     SLOW_DCHECK(to_space_.target_capacity() == from_space_.target_capacity());
283     return (to_space_.target_capacity() / Page::kPageSize) *
284            MemoryChunkLayout::AllocatableMemoryInDataPage();
285   }
286 
287   // Return the current size of a semispace, allocatable and non-allocatable
288   // memory.
289   size_t TotalCapacity() const {
290     DCHECK(to_space_.target_capacity() == from_space_.target_capacity());
291     return to_space_.target_capacity();
292   }
293 
294   // Committed memory for NewSpace is the committed memory of both semi-spaces
295   // combined.
296   size_t CommittedMemory() const final {
297     return from_space_.CommittedMemory() + to_space_.CommittedMemory();
298   }
299 
300   size_t MaximumCommittedMemory() const final {
301     return from_space_.MaximumCommittedMemory() +
302            to_space_.MaximumCommittedMemory();
303   }
304 
305   // Approximate amount of physical memory committed for this space.
306   size_t CommittedPhysicalMemory() const final;
307 
308   // Return the available bytes without growing.
309   size_t Available() const final {
310     DCHECK_GE(Capacity(), Size());
311     return Capacity() - Size();
312   }
313 
314   size_t ExternalBackingStoreBytes(ExternalBackingStoreType type) const final {
315     if (type == ExternalBackingStoreType::kArrayBuffer)
316       return heap()->YoungArrayBufferBytes();
317     DCHECK_EQ(0, from_space_.ExternalBackingStoreBytes(type));
318     return to_space_.ExternalBackingStoreBytes(type);
319   }
320 
321   size_t ExternalBackingStoreBytes() const {
322     size_t result = 0;
323     for (int i = 0; i < ExternalBackingStoreType::kNumTypes; i++) {
324       result +=
325           ExternalBackingStoreBytes(static_cast<ExternalBackingStoreType>(i));
326     }
327     return result;
328   }
329 
330   size_t AllocatedSinceLastGC() const {
331     const Address age_mark = to_space_.age_mark();
332     DCHECK_NE(age_mark, kNullAddress);
333     DCHECK_NE(top(), kNullAddress);
334     Page* const age_mark_page = Page::FromAllocationAreaAddress(age_mark);
335     Page* const last_page = Page::FromAllocationAreaAddress(top());
336     Page* current_page = age_mark_page;
337     size_t allocated = 0;
338     if (current_page != last_page) {
339       DCHECK_EQ(current_page, age_mark_page);
340       DCHECK_GE(age_mark_page->area_end(), age_mark);
341       allocated += age_mark_page->area_end() - age_mark;
342       current_page = current_page->next_page();
343     } else {
344       DCHECK_GE(top(), age_mark);
345       return top() - age_mark;
346     }
347     while (current_page != last_page) {
348       DCHECK_NE(current_page, age_mark_page);
349       allocated += MemoryChunkLayout::AllocatableMemoryInDataPage();
350       current_page = current_page->next_page();
351     }
352     DCHECK_GE(top(), current_page->area_start());
353     allocated += top() - current_page->area_start();
354     DCHECK_LE(allocated, Size());
355     return allocated;
356   }
357 
358   void MovePageFromSpaceToSpace(Page* page) {
359     DCHECK(page->IsFromPage());
360     from_space_.RemovePage(page);
361     to_space_.PrependPage(page);
362   }
363 
364   bool Rebalance();
365 
366   // Return the maximum capacity of a semispace.
367   size_t MaximumCapacity() const {
368     DCHECK(to_space_.maximum_capacity() == from_space_.maximum_capacity());
369     return to_space_.maximum_capacity();
370   }
371 
372   bool IsAtMaximumCapacity() const {
373     return TotalCapacity() == MaximumCapacity();
374   }
375 
376   // Returns the initial capacity of a semispace.
377   size_t InitialTotalCapacity() const {
378     DCHECK(to_space_.minimum_capacity() == from_space_.minimum_capacity());
379     return to_space_.minimum_capacity();
380   }
381 
382 #if DEBUG
383   void VerifyTop() const;
384 #endif  // DEBUG
385 
386   Address original_top_acquire() const {
387     return original_top_.load(std::memory_order_acquire);
388   }
389   Address original_limit_relaxed() const {
390     return original_limit_.load(std::memory_order_relaxed);
391   }
392 
393   // Return the address of the first allocatable address in the active
394   // semispace. This may be the address where the first object resides.
395   Address first_allocatable_address() const { return to_space_.space_start(); }
396 
397   // Get the age mark of the inactive semispace.
398   Address age_mark() const { return from_space_.age_mark(); }
399   // Set the age mark in the active semispace.
400   void set_age_mark(Address mark) { to_space_.set_age_mark(mark); }
401 
402   V8_WARN_UNUSED_RESULT inline AllocationResult AllocateRawSynchronized(
403       int size_in_bytes, AllocationAlignment alignment,
404       AllocationOrigin origin = AllocationOrigin::kRuntime);
405 
406   // Reset the allocation pointer to the beginning of the active semispace.
407   void ResetLinearAllocationArea();
408 
409   // When inline allocation stepping is active, either because of incremental
410   // marking, idle scavenge, or allocation statistics gathering, we 'interrupt'
411   // inline allocation every once in a while. This is done by setting
412   // allocation_info_.limit to be lower than the actual limit and and increasing
413   // it in steps to guarantee that the observers are notified periodically.
414   void UpdateInlineAllocationLimit(size_t size_in_bytes) override;
415 
416   inline bool ToSpaceContainsSlow(Address a) const;
417   inline bool ToSpaceContains(Object o) const;
418   inline bool FromSpaceContains(Object o) const;
419 
420   // Try to switch the active semispace to a new, empty, page.
421   // Returns false if this isn't possible or reasonable (i.e., there
422   // are no pages, or the current page is already empty), or true
423   // if successful.
424   bool AddFreshPage();
425   bool AddFreshPageSynchronized();
426 
427   bool AddParkedAllocationBuffer(int size_in_bytes,
428                                  AllocationAlignment alignment);
429 
430 #ifdef VERIFY_HEAP
431   // Verify the active semispace.
432   virtual void Verify(Isolate* isolate) const;
433 #endif
434 
435 #ifdef DEBUG
436   // Print the active semispace.
437   void Print() override { to_space_.Print(); }
438 #endif
439 
440   // Return whether the operation succeeded.
441   bool CommitFromSpaceIfNeeded() {
442     if (from_space_.IsCommitted()) return true;
443     return from_space_.Commit();
444   }
445 
446   bool UncommitFromSpace() {
447     if (!from_space_.IsCommitted()) return true;
448     return from_space_.Uncommit();
449   }
450 
451   bool IsFromSpaceCommitted() const { return from_space_.IsCommitted(); }
452 
453   SemiSpace* active_space() { return &to_space_; }
454 
455   Page* first_page() override { return to_space_.first_page(); }
456   Page* last_page() override { return to_space_.last_page(); }
457 
458   const Page* first_page() const override { return to_space_.first_page(); }
459   const Page* last_page() const override { return to_space_.last_page(); }
460 
461   iterator begin() { return to_space_.begin(); }
462   iterator end() { return to_space_.end(); }
463 
464   const_iterator begin() const { return to_space_.begin(); }
465   const_iterator end() const { return to_space_.end(); }
466 
467   std::unique_ptr<ObjectIterator> GetObjectIterator(Heap* heap) override;
468 
469   SemiSpace& from_space() { return from_space_; }
470   SemiSpace& to_space() { return to_space_; }
471 
472   void MoveOriginalTopForward() {
473     base::SharedMutexGuard<base::kExclusive> guard(&pending_allocation_mutex_);
474     DCHECK_GE(top(), original_top_);
475     DCHECK_LE(top(), original_limit_);
476     original_top_.store(top(), std::memory_order_release);
477   }
478 
479   void MaybeFreeUnusedLab(LinearAllocationArea info);
480 
481   base::SharedMutex* pending_allocation_mutex() {
482     return &pending_allocation_mutex_;
483   }
484 
485   // Creates a filler object in the linear allocation area.
486   void MakeLinearAllocationAreaIterable();
487 
488   // Creates a filler object in the linear allocation area and closes it.
489   void FreeLinearAllocationArea() override;
490 
491  private:
492   static const int kAllocationBufferParkingThreshold = 4 * KB;
493 
494   // Update linear allocation area to match the current to-space page.
495   void UpdateLinearAllocationArea(Address known_top = 0);
496 
497   base::Mutex mutex_;
498 
499   // The top and the limit at the time of setting the linear allocation area.
500   // These values can be accessed by background tasks. Protected by
501   // pending_allocation_mutex_.
502   std::atomic<Address> original_top_;
503   std::atomic<Address> original_limit_;
504 
505   // Protects original_top_ and original_limit_.
506   base::SharedMutex pending_allocation_mutex_;
507 
508   // The semispaces.
509   SemiSpace to_space_;
510   SemiSpace from_space_;
511   VirtualMemory reservation_;
512 
513   ParkedAllocationBuffersVector parked_allocation_buffers_;
514 
515   bool EnsureAllocation(int size_in_bytes, AllocationAlignment alignment,
516                         AllocationOrigin origin,
517                         int* out_max_aligned_size) final;
518   bool SupportsAllocationObserver() const override { return true; }
519 
520   friend class SemiSpaceObjectIterator;
521 };
522 
523 // For contiguous spaces, top should be in the space (or at the end) and limit
524 // should be the end of the space.
525 #define DCHECK_SEMISPACE_ALLOCATION_INFO(info, space) \
526   SLOW_DCHECK((space).page_low() <= (info)->top() &&  \
527               (info)->top() <= (space).page_high() && \
528               (info)->limit() <= (space).page_high())
529 
530 }  // namespace internal
531 }  // namespace v8
532 
533 #endif  // V8_HEAP_NEW_SPACES_H_
534