summaryrefslogtreecommitdiffstats
path: root/chromium/v8/src/heap/new-spaces.h
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/v8/src/heap/new-spaces.h')
-rw-r--r--chromium/v8/src/heap/new-spaces.h501
1 files changed, 501 insertions, 0 deletions
diff --git a/chromium/v8/src/heap/new-spaces.h b/chromium/v8/src/heap/new-spaces.h
new file mode 100644
index 00000000000..73613152fa0
--- /dev/null
+++ b/chromium/v8/src/heap/new-spaces.h
@@ -0,0 +1,501 @@
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef V8_HEAP_NEW_SPACES_H_
+#define V8_HEAP_NEW_SPACES_H_
+
+#include <atomic>
+#include <memory>
+
+#include "src/base/macros.h"
+#include "src/base/platform/mutex.h"
+#include "src/heap/heap.h"
+#include "src/heap/spaces.h"
+#include "src/logging/log.h"
+#include "src/objects/heap-object.h"
+
+namespace v8 {
+namespace internal {
+
+class Heap;
+class MemoryChunk;
+
+enum SemiSpaceId { kFromSpace = 0, kToSpace = 1 };
+
+// -----------------------------------------------------------------------------
+// SemiSpace in young generation
+//
+// A SemiSpace is a contiguous chunk of memory holding page-like memory chunks.
+// The mark-compact collector uses the memory of the first page in the from
+// space as a marking stack when tracing live objects.
+class SemiSpace : public Space {
+ public:
+ using iterator = PageIterator;
+ using const_iterator = ConstPageIterator;
+
+ static void Swap(SemiSpace* from, SemiSpace* to);
+
+ SemiSpace(Heap* heap, SemiSpaceId semispace)
+ : Space(heap, NEW_SPACE, new NoFreeList()),
+ current_capacity_(0),
+ maximum_capacity_(0),
+ minimum_capacity_(0),
+ age_mark_(kNullAddress),
+ committed_(false),
+ id_(semispace),
+ current_page_(nullptr),
+ pages_used_(0) {}
+
+ inline bool Contains(HeapObject o) const;
+ inline bool Contains(Object o) const;
+ inline bool ContainsSlow(Address a) const;
+
+ void SetUp(size_t initial_capacity, size_t maximum_capacity);
+ void TearDown();
+
+ bool Commit();
+ bool Uncommit();
+ bool is_committed() { return committed_; }
+
+ // Grow the semispace to the new capacity. The new capacity requested must
+ // be larger than the current capacity and less than the maximum capacity.
+ bool GrowTo(size_t new_capacity);
+
+ // Shrinks the semispace to the new capacity. The new capacity requested
+ // must be more than the amount of used memory in the semispace and less
+ // than the current capacity.
+ bool ShrinkTo(size_t new_capacity);
+
+ bool EnsureCurrentCapacity();
+
+ Address space_end() { return memory_chunk_list_.back()->area_end(); }
+
+ // Returns the start address of the first page of the space.
+ Address space_start() {
+ DCHECK_NE(memory_chunk_list_.front(), nullptr);
+ return memory_chunk_list_.front()->area_start();
+ }
+
+ Page* current_page() { return current_page_; }
+ int pages_used() { return pages_used_; }
+
+ // Returns the start address of the current page of the space.
+ Address page_low() { return current_page_->area_start(); }
+
+ // Returns one past the end address of the current page of the space.
+ Address page_high() { return current_page_->area_end(); }
+
+ bool AdvancePage() {
+ Page* next_page = current_page_->next_page();
+ // We cannot expand if we reached the maximum number of pages already. Note
+ // that we need to account for the next page already for this check as we
+ // could potentially fill the whole page after advancing.
+ const bool reached_max_pages = (pages_used_ + 1) == max_pages();
+ if (next_page == nullptr || reached_max_pages) {
+ return false;
+ }
+ current_page_ = next_page;
+ pages_used_++;
+ return true;
+ }
+
+ // Resets the space to using the first page.
+ void Reset();
+
+ void RemovePage(Page* page);
+ void PrependPage(Page* page);
+
+ Page* InitializePage(MemoryChunk* chunk);
+
+ // Age mark accessors.
+ Address age_mark() { return age_mark_; }
+ void set_age_mark(Address mark);
+
+ // Returns the current capacity of the semispace.
+ size_t current_capacity() { return current_capacity_; }
+
+ // Returns the maximum capacity of the semispace.
+ size_t maximum_capacity() { return maximum_capacity_; }
+
+ // Returns the initial capacity of the semispace.
+ size_t minimum_capacity() { return minimum_capacity_; }
+
+ SemiSpaceId id() { return id_; }
+
+ // Approximate amount of physical memory committed for this space.
+ size_t CommittedPhysicalMemory() override;
+
+ // If we don't have these here then SemiSpace will be abstract. However
+ // they should never be called:
+
+ size_t Size() override { UNREACHABLE(); }
+
+ size_t SizeOfObjects() override { return Size(); }
+
+ size_t Available() override { UNREACHABLE(); }
+
+ Page* first_page() { return reinterpret_cast<Page*>(Space::first_page()); }
+ Page* last_page() { return reinterpret_cast<Page*>(Space::last_page()); }
+
+ const Page* first_page() const {
+ return reinterpret_cast<const Page*>(Space::first_page());
+ }
+ const Page* last_page() const {
+ return reinterpret_cast<const Page*>(Space::last_page());
+ }
+
+ iterator begin() { return iterator(first_page()); }
+ iterator end() { return iterator(nullptr); }
+
+ const_iterator begin() const { return const_iterator(first_page()); }
+ const_iterator end() const { return const_iterator(nullptr); }
+
+ std::unique_ptr<ObjectIterator> GetObjectIterator(Heap* heap) override;
+
+#ifdef DEBUG
+ V8_EXPORT_PRIVATE void Print() override;
+ // Validate a range of of addresses in a SemiSpace.
+ // The "from" address must be on a page prior to the "to" address,
+ // in the linked page order, or it must be earlier on the same page.
+ static void AssertValidRange(Address from, Address to);
+#else
+ // Do nothing.
+ inline static void AssertValidRange(Address from, Address to) {}
+#endif
+
+#ifdef VERIFY_HEAP
+ virtual void Verify();
+#endif
+
+ private:
+ void RewindPages(int num_pages);
+
+ inline int max_pages() {
+ return static_cast<int>(current_capacity_ / Page::kPageSize);
+ }
+
+ // Copies the flags into the masked positions on all pages in the space.
+ void FixPagesFlags(intptr_t flags, intptr_t flag_mask);
+
+ // The currently committed space capacity.
+ size_t current_capacity_;
+
+ // The maximum capacity that can be used by this space. A space cannot grow
+ // beyond that size.
+ size_t maximum_capacity_;
+
+ // The minimum capacity for the space. A space cannot shrink below this size.
+ size_t minimum_capacity_;
+
+ // Used to govern object promotion during mark-compact collection.
+ Address age_mark_;
+
+ bool committed_;
+ SemiSpaceId id_;
+
+ Page* current_page_;
+
+ int pages_used_;
+
+ friend class NewSpace;
+ friend class SemiSpaceObjectIterator;
+};
+
+// A SemiSpaceObjectIterator is an ObjectIterator that iterates over the active
+// semispace of the heap's new space. It iterates over the objects in the
+// semispace from a given start address (defaulting to the bottom of the
+// semispace) to the top of the semispace. New objects allocated after the
+// iterator is created are not iterated.
+class SemiSpaceObjectIterator : public ObjectIterator {
+ public:
+ // Create an iterator over the allocated objects in the given to-space.
+ explicit SemiSpaceObjectIterator(NewSpace* space);
+
+ inline HeapObject Next() override;
+
+ private:
+ void Initialize(Address start, Address end);
+
+ // The current iteration point.
+ Address current_;
+ // The end of iteration.
+ Address limit_;
+};
+
+// -----------------------------------------------------------------------------
+// The young generation space.
+//
+// The new space consists of a contiguous pair of semispaces. It simply
+// forwards most functions to the appropriate semispace.
+
+class V8_EXPORT_PRIVATE NewSpace
+ : NON_EXPORTED_BASE(public SpaceWithLinearArea) {
+ public:
+ using iterator = PageIterator;
+ using const_iterator = ConstPageIterator;
+
+ NewSpace(Heap* heap, v8::PageAllocator* page_allocator,
+ size_t initial_semispace_capacity, size_t max_semispace_capacity);
+
+ ~NewSpace() override { TearDown(); }
+
+ inline bool ContainsSlow(Address a) const;
+ inline bool Contains(Object o) const;
+ inline bool Contains(HeapObject o) const;
+
+ // Tears down the space. Heap memory was not allocated by the space, so it
+ // is not deallocated here.
+ void TearDown();
+
+ // Flip the pair of spaces.
+ void Flip();
+
+ // Grow the capacity of the semispaces. Assumes that they are not at
+ // their maximum capacity.
+ void Grow();
+
+ // Shrink the capacity of the semispaces.
+ void Shrink();
+
+ // Return the allocated bytes in the active semispace.
+ size_t Size() final {
+ DCHECK_GE(top(), to_space_.page_low());
+ return to_space_.pages_used() *
+ MemoryChunkLayout::AllocatableMemoryInDataPage() +
+ static_cast<size_t>(top() - to_space_.page_low());
+ }
+
+ size_t SizeOfObjects() final { return Size(); }
+
+ // Return the allocatable capacity of a semispace.
+ size_t Capacity() {
+ SLOW_DCHECK(to_space_.current_capacity() == from_space_.current_capacity());
+ return (to_space_.current_capacity() / Page::kPageSize) *
+ MemoryChunkLayout::AllocatableMemoryInDataPage();
+ }
+
+ // Return the current size of a semispace, allocatable and non-allocatable
+ // memory.
+ size_t TotalCapacity() {
+ DCHECK(to_space_.current_capacity() == from_space_.current_capacity());
+ return to_space_.current_capacity();
+ }
+
+ // Committed memory for NewSpace is the committed memory of both semi-spaces
+ // combined.
+ size_t CommittedMemory() final {
+ return from_space_.CommittedMemory() + to_space_.CommittedMemory();
+ }
+
+ size_t MaximumCommittedMemory() final {
+ return from_space_.MaximumCommittedMemory() +
+ to_space_.MaximumCommittedMemory();
+ }
+
+ // Approximate amount of physical memory committed for this space.
+ size_t CommittedPhysicalMemory() final;
+
+ // Return the available bytes without growing.
+ size_t Available() final {
+ DCHECK_GE(Capacity(), Size());
+ return Capacity() - Size();
+ }
+
+ size_t ExternalBackingStoreBytes(ExternalBackingStoreType type) const final {
+ if (V8_ARRAY_BUFFER_EXTENSION_BOOL &&
+ type == ExternalBackingStoreType::kArrayBuffer)
+ return heap()->YoungArrayBufferBytes();
+ DCHECK_EQ(0, from_space_.ExternalBackingStoreBytes(type));
+ return to_space_.ExternalBackingStoreBytes(type);
+ }
+
+ size_t ExternalBackingStoreBytes() {
+ size_t result = 0;
+ for (int i = 0; i < ExternalBackingStoreType::kNumTypes; i++) {
+ result +=
+ ExternalBackingStoreBytes(static_cast<ExternalBackingStoreType>(i));
+ }
+ return result;
+ }
+
+ size_t AllocatedSinceLastGC() {
+ const Address age_mark = to_space_.age_mark();
+ DCHECK_NE(age_mark, kNullAddress);
+ DCHECK_NE(top(), kNullAddress);
+ Page* const age_mark_page = Page::FromAllocationAreaAddress(age_mark);
+ Page* const last_page = Page::FromAllocationAreaAddress(top());
+ Page* current_page = age_mark_page;
+ size_t allocated = 0;
+ if (current_page != last_page) {
+ DCHECK_EQ(current_page, age_mark_page);
+ DCHECK_GE(age_mark_page->area_end(), age_mark);
+ allocated += age_mark_page->area_end() - age_mark;
+ current_page = current_page->next_page();
+ } else {
+ DCHECK_GE(top(), age_mark);
+ return top() - age_mark;
+ }
+ while (current_page != last_page) {
+ DCHECK_NE(current_page, age_mark_page);
+ allocated += MemoryChunkLayout::AllocatableMemoryInDataPage();
+ current_page = current_page->next_page();
+ }
+ DCHECK_GE(top(), current_page->area_start());
+ allocated += top() - current_page->area_start();
+ DCHECK_LE(allocated, Size());
+ return allocated;
+ }
+
+ void MovePageFromSpaceToSpace(Page* page) {
+ DCHECK(page->IsFromPage());
+ from_space_.RemovePage(page);
+ to_space_.PrependPage(page);
+ }
+
+ bool Rebalance();
+
+ // Return the maximum capacity of a semispace.
+ size_t MaximumCapacity() {
+ DCHECK(to_space_.maximum_capacity() == from_space_.maximum_capacity());
+ return to_space_.maximum_capacity();
+ }
+
+ bool IsAtMaximumCapacity() { return TotalCapacity() == MaximumCapacity(); }
+
+ // Returns the initial capacity of a semispace.
+ size_t InitialTotalCapacity() {
+ DCHECK(to_space_.minimum_capacity() == from_space_.minimum_capacity());
+ return to_space_.minimum_capacity();
+ }
+
+ void ResetOriginalTop() {
+ DCHECK_GE(top(), original_top_);
+ DCHECK_LE(top(), original_limit_);
+ original_top_.store(top(), std::memory_order_release);
+ }
+
+ Address original_top_acquire() {
+ return original_top_.load(std::memory_order_acquire);
+ }
+ Address original_limit_relaxed() {
+ return original_limit_.load(std::memory_order_relaxed);
+ }
+
+ // Return the address of the first allocatable address in the active
+ // semispace. This may be the address where the first object resides.
+ Address first_allocatable_address() { return to_space_.space_start(); }
+
+ // Get the age mark of the inactive semispace.
+ Address age_mark() { return from_space_.age_mark(); }
+ // Set the age mark in the active semispace.
+ void set_age_mark(Address mark) { to_space_.set_age_mark(mark); }
+
+ V8_WARN_UNUSED_RESULT V8_INLINE AllocationResult
+ AllocateRawAligned(int size_in_bytes, AllocationAlignment alignment,
+ AllocationOrigin origin = AllocationOrigin::kRuntime);
+
+ V8_WARN_UNUSED_RESULT V8_INLINE AllocationResult AllocateRawUnaligned(
+ int size_in_bytes, AllocationOrigin origin = AllocationOrigin::kRuntime);
+
+ V8_WARN_UNUSED_RESULT V8_INLINE AllocationResult
+ AllocateRaw(int size_in_bytes, AllocationAlignment alignment,
+ AllocationOrigin origin = AllocationOrigin::kRuntime);
+
+ V8_WARN_UNUSED_RESULT inline AllocationResult AllocateRawSynchronized(
+ int size_in_bytes, AllocationAlignment alignment,
+ AllocationOrigin origin = AllocationOrigin::kRuntime);
+
+ // Reset the allocation pointer to the beginning of the active semispace.
+ void ResetLinearAllocationArea();
+
+ // When inline allocation stepping is active, either because of incremental
+ // marking, idle scavenge, or allocation statistics gathering, we 'interrupt'
+ // inline allocation every once in a while. This is done by setting
+ // allocation_info_.limit to be lower than the actual limit and and increasing
+ // it in steps to guarantee that the observers are notified periodically.
+ void UpdateInlineAllocationLimit(size_t size_in_bytes) override;
+
+ inline bool ToSpaceContainsSlow(Address a) const;
+ inline bool ToSpaceContains(Object o) const;
+ inline bool FromSpaceContains(Object o) const;
+
+ // Try to switch the active semispace to a new, empty, page.
+ // Returns false if this isn't possible or reasonable (i.e., there
+ // are no pages, or the current page is already empty), or true
+ // if successful.
+ bool AddFreshPage();
+ bool AddFreshPageSynchronized();
+
+#ifdef VERIFY_HEAP
+ // Verify the active semispace.
+ virtual void Verify(Isolate* isolate);
+#endif
+
+#ifdef DEBUG
+ // Print the active semispace.
+ void Print() override { to_space_.Print(); }
+#endif
+
+ // Return whether the operation succeeded.
+ bool CommitFromSpaceIfNeeded() {
+ if (from_space_.is_committed()) return true;
+ return from_space_.Commit();
+ }
+
+ bool UncommitFromSpace() {
+ if (!from_space_.is_committed()) return true;
+ return from_space_.Uncommit();
+ }
+
+ bool IsFromSpaceCommitted() { return from_space_.is_committed(); }
+
+ SemiSpace* active_space() { return &to_space_; }
+
+ Page* first_page() { return to_space_.first_page(); }
+ Page* last_page() { return to_space_.last_page(); }
+
+ iterator begin() { return to_space_.begin(); }
+ iterator end() { return to_space_.end(); }
+
+ const_iterator begin() const { return to_space_.begin(); }
+ const_iterator end() const { return to_space_.end(); }
+
+ std::unique_ptr<ObjectIterator> GetObjectIterator(Heap* heap) override;
+
+ SemiSpace& from_space() { return from_space_; }
+ SemiSpace& to_space() { return to_space_; }
+
+ private:
+ // Update linear allocation area to match the current to-space page.
+ void UpdateLinearAllocationArea();
+
+ base::Mutex mutex_;
+
+ // The top and the limit at the time of setting the linear allocation area.
+ // These values can be accessed by background tasks.
+ std::atomic<Address> original_top_;
+ std::atomic<Address> original_limit_;
+
+ // The semispaces.
+ SemiSpace to_space_;
+ SemiSpace from_space_;
+ VirtualMemory reservation_;
+
+ bool EnsureAllocation(int size_in_bytes, AllocationAlignment alignment);
+ bool SupportsInlineAllocation() override { return true; }
+
+ friend class SemiSpaceObjectIterator;
+};
+
+// For contiguous spaces, top should be in the space (or at the end) and limit
+// should be the end of the space.
+#define DCHECK_SEMISPACE_ALLOCATION_INFO(info, space) \
+ SLOW_DCHECK((space).page_low() <= (info).top() && \
+ (info).top() <= (space).page_high() && \
+ (info).limit() <= (space).page_high())
+
+} // namespace internal
+} // namespace v8
+
+#endif // V8_HEAP_NEW_SPACES_H_