summaryrefslogtreecommitdiff
path: root/deps/v8/src/spaces.h
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/spaces.h')
-rw-r--r--deps/v8/src/spaces.h1703
1 files changed, 1703 insertions, 0 deletions
diff --git a/deps/v8/src/spaces.h b/deps/v8/src/spaces.h
new file mode 100644
index 000000000..3138cc360
--- /dev/null
+++ b/deps/v8/src/spaces.h
@@ -0,0 +1,1703 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_SPACES_H_
+#define V8_SPACES_H_
+
+#include "list-inl.h"
+#include "log.h"
+
+namespace v8 { namespace internal {
+
+// -----------------------------------------------------------------------------
+// Heap structures:
+//
+// A JS heap consists of a young generation, an old generation, and a large
+// object space. The young generation is divided into two semispaces. A
+// scavenger implements Cheney's copying algorithm. The old generation is
+// separated into a map space and an old object space. The map space contains
+// all (and only) map objects, the rest of old objects go into the old space.
+// The old generation is collected by a mark-sweep-compact collector.
+//
+// The semispaces of the young generation are contiguous. The old and map
+// spaces consists of a list of pages. A page has a page header, a remembered
+// set area, and an object area. A page size is deliberately chosen as 8K
+// bytes. The first word of a page is an opaque page header that has the
+// address of the next page and its ownership information. The second word may
+// have the allocation top address of this page. The next 248 bytes are
+// remembered sets. Heap objects are aligned to the pointer size (4 bytes). A
+// remembered set bit corresponds to a pointer in the object area.
+//
+// There is a separate large object space for objects larger than
+// Page::kMaxHeapObjectSize, so that they do not have to move during
+// collection. The large object space is paged and uses the same remembered
+// set implementation. Pages in large object space may be larger than 8K.
+//
+// NOTE: The mark-compact collector rebuilds the remembered set after a
+// collection. It reuses first a few words of the remembered set for
+// bookkeeping relocation information.
+
+
+// Some assertion macros used in the debugging mode.
+
+#define ASSERT_PAGE_ALIGNED(address) \
+ ASSERT((OffsetFrom(address) & Page::kPageAlignmentMask) == 0)
+
+#define ASSERT_OBJECT_ALIGNED(address) \
+ ASSERT((OffsetFrom(address) & kObjectAlignmentMask) == 0)
+
+#define ASSERT_OBJECT_SIZE(size) \
+ ASSERT((0 < size) && (size <= Page::kMaxHeapObjectSize))
+
+#define ASSERT_PAGE_OFFSET(offset) \
+ ASSERT((Page::kObjectStartOffset <= offset) \
+ && (offset <= Page::kPageSize))
+
+#define ASSERT_MAP_PAGE_INDEX(index) \
+ ASSERT((0 <= index) && (index <= MapSpace::kMaxMapPageIndex))
+
+
+class PagedSpace;
+class MemoryAllocator;
+class AllocationInfo;
+
+// -----------------------------------------------------------------------------
+// A page normally has 8K bytes. Large object pages may be larger. A page
+// address is always aligned to the 8K page size. A page is divided into
+// three areas: the first two words are used for bookkeeping, the next 248
+// bytes are used as remembered set, and the rest of the page is the object
+// area.
+//
+// Pointers are aligned to the pointer size (4 bytes), only 1 bit is needed
+// for a pointer in the remembered set. Given an address, its remembered set
+// bit position (offset from the start of the page) is calculated by dividing
+// its page offset by 32. Therefore, the object area in a page starts at the
+// 256th byte (8K/32). Bytes 0 to 255 do not need the remembered set, so that
+// the first two words (64 bits) in a page can be used for other purposes.
+//
+// The mark-compact collector transforms a map pointer into a page index and a
+// page offset. The map space can have up to 1024 pages, and 8M bytes (1024 *
+// 8K) in total. Because a map pointer is aligned to the pointer size (4
+// bytes), 11 bits are enough to encode the page offset. 21 bits (10 for the
+// page index + 11 for the offset in the page) are required to encode a map
+// pointer.
+//
+// The only way to get a page pointer is by calling factory methods:
+// Page* p = Page::FromAddress(addr); or
+// Page* p = Page::FromAllocationTop(top);
+class Page {
+ public:
+ // Returns the page containing a given address. The address ranges
+ // from [page_addr .. page_addr + kPageSize[
+ //
+ // Note that this function only works for addresses in normal paged
+ // spaces and addresses in the first 8K of large object pages (ie,
+ // the start of large objects but not necessarily derived pointers
+ // within them).
+ INLINE(static Page* FromAddress(Address a)) {
+ return reinterpret_cast<Page*>(OffsetFrom(a) & ~kPageAlignmentMask);
+ }
+
+ // Returns the page containing an allocation top. Because an allocation
+ // top address can be the upper bound of the page, we need to subtract
+ // it with kPointerSize first. The address ranges from
+ // [page_addr + kObjectStartOffset .. page_addr + kPageSize].
+ INLINE(static Page* FromAllocationTop(Address top)) {
+ Page* p = FromAddress(top - kPointerSize);
+ ASSERT_PAGE_OFFSET(p->Offset(top));
+ return p;
+ }
+
+ // Returns the start address of this page.
+ Address address() { return reinterpret_cast<Address>(this); }
+
+ // Checks whether this is a valid page address.
+ bool is_valid() { return address() != NULL; }
+
+ // Returns the next page of this page.
+ inline Page* next_page();
+
+ // Return the end of allocation in this page. Undefined for unused pages.
+ inline Address AllocationTop();
+
+ // Returns the start address of the object area in this page.
+ Address ObjectAreaStart() { return address() + kObjectStartOffset; }
+
+ // Returns the end address (exclusive) of the object area in this page.
+ Address ObjectAreaEnd() { return address() + Page::kPageSize; }
+
+ // Returns the start address of the remembered set area.
+ Address RSetStart() { return address() + kRSetStartOffset; }
+
+ // Returns the end address of the remembered set area (exclusive).
+ Address RSetEnd() { return address() + kRSetEndOffset; }
+
+ // Checks whether an address is page aligned.
+ static bool IsAlignedToPageSize(Address a) {
+ return 0 == (OffsetFrom(a) & kPageAlignmentMask);
+ }
+
+ // True if this page is a large object page.
+ bool IsLargeObjectPage() { return (is_normal_page & 0x1) == 0; }
+
+ // Returns the offset of a given address to this page.
+ INLINE(int Offset(Address a)) {
+ int offset = a - address();
+ ASSERT_PAGE_OFFSET(offset);
+ return offset;
+ }
+
+ // Returns the address for a given offset to the this page.
+ Address OffsetToAddress(int offset) {
+ ASSERT_PAGE_OFFSET(offset);
+ return address() + offset;
+ }
+
+ // ---------------------------------------------------------------------
+ // Remembered set support
+
+ // Clears remembered set in this page.
+ inline void ClearRSet();
+
+ // Return the address of the remembered set word corresponding to an
+ // object address/offset pair, and the bit encoded as a single-bit
+ // mask in the output parameter 'bitmask'.
+ INLINE(static Address ComputeRSetBitPosition(Address address, int offset,
+ uint32_t* bitmask));
+
+ // Sets the corresponding remembered set bit for a given address.
+ INLINE(static void SetRSet(Address address, int offset));
+
+ // Clears the corresponding remembered set bit for a given address.
+ static inline void UnsetRSet(Address address, int offset);
+
+ // Checks whether the remembered set bit for a given address is set.
+ static inline bool IsRSetSet(Address address, int offset);
+
+#ifdef DEBUG
+ // Use a state to mark whether remembered set space can be used for other
+ // purposes.
+ enum RSetState { IN_USE, NOT_IN_USE };
+ static bool is_rset_in_use() { return rset_state_ == IN_USE; }
+ static void set_rset_state(RSetState state) { rset_state_ = state; }
+#endif
+
+ // 8K bytes per page.
+ static const int kPageSizeBits = 13;
+
+ // Page size in bytes. This must be a multiple of the OS page size.
+ static const int kPageSize = 1 << kPageSizeBits;
+
+ // Page size mask.
+ static const int kPageAlignmentMask = (1 << kPageSizeBits) - 1;
+
+ // The end offset of the remembered set in a page
+ // (heaps are aligned to pointer size).
+ static const int kRSetEndOffset= kPageSize / kBitsPerPointer;
+
+ // The start offset of the remembered set in a page.
+ static const int kRSetStartOffset = kRSetEndOffset / kBitsPerPointer;
+
+ // The start offset of the object area in a page.
+ static const int kObjectStartOffset = kRSetEndOffset;
+
+ // Object area size in bytes.
+ static const int kObjectAreaSize = kPageSize - kObjectStartOffset;
+
+ // Maximum object size that fits in a page.
+ static const int kMaxHeapObjectSize = kObjectAreaSize;
+
+ //---------------------------------------------------------------------------
+ // Page header description.
+ //
+ // If a page is not in the large object space, the first word,
+ // opaque_header, encodes the next page address (aligned to kPageSize 8K)
+ // and the chunk number (0 ~ 8K-1). Only MemoryAllocator should use
+ // opaque_header. The value range of the opaque_header is [0..kPageSize[,
+ // or [next_page_start, next_page_end[. It cannot point to a valid address
+ // in the current page. If a page is in the large object space, the first
+ // word *may* (if the page start and large object chunk start are the
+ // same) contain the address of the next large object chunk.
+ int opaque_header;
+
+ // If the page is not in the large object space, the low-order bit of the
+ // second word is set. If the page is in the large object space, the
+ // second word *may* (if the page start and large object chunk start are
+ // the same) contain the large object chunk size. In either case, the
+ // low-order bit for large object pages will be cleared.
+ int is_normal_page;
+
+ // The following fields overlap with remembered set, they can only
+ // be used in the mark-compact collector when remembered set is not
+ // used.
+
+ // The allocation pointer after relocating objects to this page.
+ Address mc_relocation_top;
+
+ // The index of the page in its owner space.
+ int mc_page_index;
+
+ // The forwarding address of the first live object in this page.
+ Address mc_first_forwarded;
+
+#ifdef DEBUG
+ private:
+ static RSetState rset_state_; // state of the remembered set
+#endif
+};
+
+
+// ----------------------------------------------------------------------------
+// Space is the abstract superclass for all allocation spaces.
+class Space : public Malloced {
+ public:
+ Space(AllocationSpace id, Executability executable)
+ : id_(id), executable_(executable) {}
+
+ virtual ~Space() {}
+
+ // Does the space need executable memory?
+ Executability executable() { return executable_; }
+
+ // Identity used in error reporting.
+ AllocationSpace identity() { return id_; }
+
+ virtual int Size() = 0;
+
+#ifdef DEBUG
+ virtual void Verify() = 0;
+ virtual void Print() = 0;
+#endif
+
+ private:
+ AllocationSpace id_;
+ Executability executable_;
+};
+
+
+// ----------------------------------------------------------------------------
+// A space acquires chunks of memory from the operating system. The memory
+// allocator manages chunks for the paged heap spaces (old space and map
+// space). A paged chunk consists of pages. Pages in a chunk have contiguous
+// addresses and are linked as a list.
+//
+// The allocator keeps an initial chunk which is used for the new space. The
+// leftover regions of the initial chunk are used for the initial chunks of
+// old space and map space if they are big enough to hold at least one page.
+// The allocator assumes that there is one old space and one map space, each
+// expands the space by allocating kPagesPerChunk pages except the last
+// expansion (before running out of space). The first chunk may contain fewer
+// than kPagesPerChunk pages as well.
+//
+// The memory allocator also allocates chunks for the large object space, but
+// they are managed by the space itself. The new space does not expand.
+
+class MemoryAllocator : public AllStatic {
+ public:
+ // Initializes its internal bookkeeping structures.
+ // Max capacity of the total space.
+ static bool Setup(int max_capacity);
+
+ // Deletes valid chunks.
+ static void TearDown();
+
+ // Reserves an initial address range of virtual memory to be split between
+ // the two new space semispaces, the old space, and the map space. The
+ // memory is not yet committed or assigned to spaces and split into pages.
+ // The initial chunk is unmapped when the memory allocator is torn down.
+ // This function should only be called when there is not already a reserved
+ // initial chunk (initial_chunk_ should be NULL). It returns the start
+ // address of the initial chunk if successful, with the side effect of
+ // setting the initial chunk, or else NULL if unsuccessful and leaves the
+ // initial chunk NULL.
+ static void* ReserveInitialChunk(const size_t requested);
+
+ // Commits pages from an as-yet-unmanaged block of virtual memory into a
+ // paged space. The block should be part of the initial chunk reserved via
+ // a call to ReserveInitialChunk. The number of pages is always returned in
+ // the output parameter num_pages. This function assumes that the start
+ // address is non-null and that it is big enough to hold at least one
+ // page-aligned page. The call always succeeds, and num_pages is always
+ // greater than zero.
+ static Page* CommitPages(Address start, size_t size, PagedSpace* owner,
+ int* num_pages);
+
+ // Commit a contiguous block of memory from the initial chunk. Assumes that
+ // the address is not NULL, the size is greater than zero, and that the
+ // block is contained in the initial chunk. Returns true if it succeeded
+ // and false otherwise.
+ static bool CommitBlock(Address start, size_t size, Executability executable);
+
+ // Attempts to allocate the requested (non-zero) number of pages from the
+ // OS. Fewer pages might be allocated than requested. If it fails to
+ // allocate memory for the OS or cannot allocate a single page, this
+ // function returns an invalid page pointer (NULL). The caller must check
+ // whether the returned page is valid (by calling Page::is_valid()). It is
+ // guaranteed that allocated pages have contiguous addresses. The actual
+ // number of allocated page is returned in the output parameter
+ // allocated_pages.
+ static Page* AllocatePages(int requested_pages, int* allocated_pages,
+ PagedSpace* owner);
+
+ // Frees pages from a given page and after. If 'p' is the first page
+ // of a chunk, pages from 'p' are freed and this function returns an
+ // invalid page pointer. Otherwise, the function searches a page
+ // after 'p' that is the first page of a chunk. Pages after the
+ // found page are freed and the function returns 'p'.
+ static Page* FreePages(Page* p);
+
+ // Allocates and frees raw memory of certain size.
+ // These are just thin wrappers around OS::Allocate and OS::Free,
+ // but keep track of allocated bytes as part of heap.
+ static void* AllocateRawMemory(const size_t requested,
+ size_t* allocated,
+ Executability executable);
+ static void FreeRawMemory(void* buf, size_t length);
+
+ // Returns the maximum available bytes of heaps.
+ static int Available() { return capacity_ < size_ ? 0 : capacity_ - size_; }
+
+ // Returns maximum available bytes that the old space can have.
+ static int MaxAvailable() {
+ return (Available() / Page::kPageSize) * Page::kObjectAreaSize;
+ }
+
+ // Links two pages.
+ static inline void SetNextPage(Page* prev, Page* next);
+
+ // Returns the next page of a given page.
+ static inline Page* GetNextPage(Page* p);
+
+ // Checks whether a page belongs to a space.
+ static inline bool IsPageInSpace(Page* p, PagedSpace* space);
+
+ // Returns the space that owns the given page.
+ static inline PagedSpace* PageOwner(Page* page);
+
+ // Finds the first/last page in the same chunk as a given page.
+ static Page* FindFirstPageInSameChunk(Page* p);
+ static Page* FindLastPageInSameChunk(Page* p);
+
+#ifdef ENABLE_HEAP_PROTECTION
+ // Protect/unprotect a block of memory by marking it read-only/writable.
+ static inline void Protect(Address start, size_t size);
+ static inline void Unprotect(Address start, size_t size,
+ Executability executable);
+
+ // Protect/unprotect a chunk given a page in the chunk.
+ static inline void ProtectChunkFromPage(Page* page);
+ static inline void UnprotectChunkFromPage(Page* page);
+#endif
+
+#ifdef DEBUG
+ // Reports statistic info of the space.
+ static void ReportStatistics();
+#endif
+
+ // Due to encoding limitation, we can only have 8K chunks.
+ static const int kMaxNofChunks = 1 << Page::kPageSizeBits;
+ // If a chunk has at least 32 pages, the maximum heap size is about
+ // 8 * 1024 * 32 * 8K = 2G bytes.
+ static const int kPagesPerChunk = 64;
+ static const int kChunkSize = kPagesPerChunk * Page::kPageSize;
+
+ private:
+ // Maximum space size in bytes.
+ static int capacity_;
+
+ // Allocated space size in bytes.
+ static int size_;
+
+ // The initial chunk of virtual memory.
+ static VirtualMemory* initial_chunk_;
+
+ // Allocated chunk info: chunk start address, chunk size, and owning space.
+ class ChunkInfo BASE_EMBEDDED {
+ public:
+ ChunkInfo() : address_(NULL), size_(0), owner_(NULL) {}
+ void init(Address a, size_t s, PagedSpace* o) {
+ address_ = a;
+ size_ = s;
+ owner_ = o;
+ }
+ Address address() { return address_; }
+ size_t size() { return size_; }
+ PagedSpace* owner() { return owner_; }
+
+ private:
+ Address address_;
+ size_t size_;
+ PagedSpace* owner_;
+ };
+
+ // Chunks_, free_chunk_ids_ and top_ act as a stack of free chunk ids.
+ static List<ChunkInfo> chunks_;
+ static List<int> free_chunk_ids_;
+ static int max_nof_chunks_;
+ static int top_;
+
+ // Push/pop a free chunk id onto/from the stack.
+ static void Push(int free_chunk_id);
+ static int Pop();
+ static bool OutOfChunkIds() { return top_ == 0; }
+
+ // Frees a chunk.
+ static void DeleteChunk(int chunk_id);
+
+ // Basic check whether a chunk id is in the valid range.
+ static inline bool IsValidChunkId(int chunk_id);
+
+ // Checks whether a chunk id identifies an allocated chunk.
+ static inline bool IsValidChunk(int chunk_id);
+
+ // Returns the chunk id that a page belongs to.
+ static inline int GetChunkId(Page* p);
+
+ // True if the address lies in the initial chunk.
+ static inline bool InInitialChunk(Address address);
+
+ // Initializes pages in a chunk. Returns the first page address.
+ // This function and GetChunkId() are provided for the mark-compact
+ // collector to rebuild page headers in the from space, which is
+ // used as a marking stack and its page headers are destroyed.
+ static Page* InitializePagesInChunk(int chunk_id, int pages_in_chunk,
+ PagedSpace* owner);
+};
+
+
+// -----------------------------------------------------------------------------
+// Interface for heap object iterator to be implemented by all object space
+// object iterators.
+//
+// NOTE: The space specific object iterators also implements the own has_next()
+// and next() methods which are used to avoid using virtual functions
+// iterating a specific space.
+
+class ObjectIterator : public Malloced {
+ public:
+ virtual ~ObjectIterator() { }
+
+ virtual bool has_next_object() = 0;
+ virtual HeapObject* next_object() = 0;
+};
+
+
+// -----------------------------------------------------------------------------
+// Heap object iterator in new/old/map spaces.
+//
+// A HeapObjectIterator iterates objects from a given address to the
+// top of a space. The given address must be below the current
+// allocation pointer (space top). If the space top changes during
+// iteration (because of allocating new objects), the iterator does
+// not iterate new objects. The caller function must create a new
+// iterator starting from the old top in order to visit these new
+// objects. Heap::Scavenage() is such an example.
+
+class HeapObjectIterator: public ObjectIterator {
+ public:
+ // Creates a new object iterator in a given space. If a start
+ // address is not given, the iterator starts from the space bottom.
+ // If the size function is not given, the iterator calls the default
+ // Object::Size().
+ explicit HeapObjectIterator(PagedSpace* space);
+ HeapObjectIterator(PagedSpace* space, HeapObjectCallback size_func);
+ HeapObjectIterator(PagedSpace* space, Address start);
+ HeapObjectIterator(PagedSpace* space,
+ Address start,
+ HeapObjectCallback size_func);
+
+ inline bool has_next();
+ inline HeapObject* next();
+
+ // implementation of ObjectIterator.
+ virtual bool has_next_object() { return has_next(); }
+ virtual HeapObject* next_object() { return next(); }
+
+ private:
+ Address cur_addr_; // current iteration point
+ Address end_addr_; // end iteration point
+ Address cur_limit_; // current page limit
+ HeapObjectCallback size_func_; // size function
+ Page* end_page_; // caches the page of the end address
+
+ // Slow path of has_next, checks whether there are more objects in
+ // the next page.
+ bool HasNextInNextPage();
+
+ // Initializes fields.
+ void Initialize(Address start, Address end, HeapObjectCallback size_func);
+
+#ifdef DEBUG
+ // Verifies whether fields have valid values.
+ void Verify();
+#endif
+};
+
+
+// -----------------------------------------------------------------------------
+// A PageIterator iterates pages in a space.
+//
+// The PageIterator class provides three modes for iterating pages in a space:
+// PAGES_IN_USE iterates pages that are in use by the allocator;
+// PAGES_USED_BY_GC iterates pages that hold relocated objects during a
+// mark-compact collection;
+// ALL_PAGES iterates all pages in the space.
+
+class PageIterator BASE_EMBEDDED {
+ public:
+ enum Mode {PAGES_IN_USE, PAGES_USED_BY_MC, ALL_PAGES};
+
+ PageIterator(PagedSpace* space, Mode mode);
+
+ inline bool has_next();
+ inline Page* next();
+
+ private:
+ Page* cur_page_; // next page to return
+ Page* stop_page_; // page where to stop
+};
+
+
+// -----------------------------------------------------------------------------
+// A space has a list of pages. The next page can be accessed via
+// Page::next_page() call. The next page of the last page is an
+// invalid page pointer. A space can expand and shrink dynamically.
+
+// An abstraction of allocation and relocation pointers in a page-structured
+// space.
+class AllocationInfo {
+ public:
+ Address top; // current allocation top
+ Address limit; // current allocation limit
+
+#ifdef DEBUG
+ bool VerifyPagedAllocation() {
+ return (Page::FromAllocationTop(top) == Page::FromAllocationTop(limit))
+ && (top <= limit);
+ }
+#endif
+};
+
+
+// An abstraction of the accounting statistics of a page-structured space.
+// The 'capacity' of a space is the number of object-area bytes (ie, not
+// including page bookkeeping structures) currently in the space. The 'size'
+// of a space is the number of allocated bytes, the 'waste' in the space is
+// the number of bytes that are not allocated and not available to
+// allocation without reorganizing the space via a GC (eg, small blocks due
+// to internal fragmentation, top of page areas in map space), and the bytes
+// 'available' is the number of unallocated bytes that are not waste. The
+// capacity is the sum of size, waste, and available.
+//
+// The stats are only set by functions that ensure they stay balanced. These
+// functions increase or decrease one of the non-capacity stats in
+// conjunction with capacity, or else they always balance increases and
+// decreases to the non-capacity stats.
+class AllocationStats BASE_EMBEDDED {
+ public:
+ AllocationStats() { Clear(); }
+
+ // Zero out all the allocation statistics (ie, no capacity).
+ void Clear() {
+ capacity_ = 0;
+ available_ = 0;
+ size_ = 0;
+ waste_ = 0;
+ }
+
+ // Reset the allocation statistics (ie, available = capacity with no
+ // wasted or allocated bytes).
+ void Reset() {
+ available_ = capacity_;
+ size_ = 0;
+ waste_ = 0;
+ }
+
+ // Accessors for the allocation statistics.
+ int Capacity() { return capacity_; }
+ int Available() { return available_; }
+ int Size() { return size_; }
+ int Waste() { return waste_; }
+
+ // Grow the space by adding available bytes.
+ void ExpandSpace(int size_in_bytes) {
+ capacity_ += size_in_bytes;
+ available_ += size_in_bytes;
+ }
+
+ // Shrink the space by removing available bytes.
+ void ShrinkSpace(int size_in_bytes) {
+ capacity_ -= size_in_bytes;
+ available_ -= size_in_bytes;
+ }
+
+ // Allocate from available bytes (available -> size).
+ void AllocateBytes(int size_in_bytes) {
+ available_ -= size_in_bytes;
+ size_ += size_in_bytes;
+ }
+
+ // Free allocated bytes, making them available (size -> available).
+ void DeallocateBytes(int size_in_bytes) {
+ size_ -= size_in_bytes;
+ available_ += size_in_bytes;
+ }
+
+ // Waste free bytes (available -> waste).
+ void WasteBytes(int size_in_bytes) {
+ available_ -= size_in_bytes;
+ waste_ += size_in_bytes;
+ }
+
+ // Consider the wasted bytes to be allocated, as they contain filler
+ // objects (waste -> size).
+ void FillWastedBytes(int size_in_bytes) {
+ waste_ -= size_in_bytes;
+ size_ += size_in_bytes;
+ }
+
+ private:
+ int capacity_;
+ int available_;
+ int size_;
+ int waste_;
+};
+
+
+class PagedSpace : public Space {
+ public:
+ // Creates a space with a maximum capacity, and an id.
+ PagedSpace(int max_capacity, AllocationSpace id, Executability executable);
+
+ virtual ~PagedSpace() {}
+
+ // Set up the space using the given address range of virtual memory (from
+ // the memory allocator's initial chunk) if possible. If the block of
+ // addresses is not big enough to contain a single page-aligned page, a
+ // fresh chunk will be allocated.
+ bool Setup(Address start, size_t size);
+
+ // Returns true if the space has been successfully set up and not
+ // subsequently torn down.
+ bool HasBeenSetup();
+
+ // Cleans up the space, frees all pages in this space except those belonging
+ // to the initial chunk, uncommits addresses in the initial chunk.
+ void TearDown();
+
+ // Checks whether an object/address is in this space.
+ inline bool Contains(Address a);
+ bool Contains(HeapObject* o) { return Contains(o->address()); }
+
+ // Given an address occupied by a live object, return that object if it is
+ // in this space, or Failure::Exception() if it is not. The implementation
+ // iterates over objects in the page containing the address, the cost is
+ // linear in the number of objects in the page. It may be slow.
+ Object* FindObject(Address addr);
+
+ // Checks whether page is currently in use by this space.
+ bool IsUsed(Page* page);
+
+ // Clears remembered sets of pages in this space.
+ void ClearRSet();
+
+ // Prepares for a mark-compact GC.
+ virtual void PrepareForMarkCompact(bool will_compact) = 0;
+
+ virtual Address PageAllocationTop(Page* page) = 0;
+
+ // Current capacity without growing (Size() + Available() + Waste()).
+ int Capacity() { return accounting_stats_.Capacity(); }
+
+ // Available bytes without growing.
+ int Available() { return accounting_stats_.Available(); }
+
+ // Allocated bytes in this space.
+ virtual int Size() { return accounting_stats_.Size(); }
+
+ // Wasted bytes due to fragmentation and not recoverable until the
+ // next GC of this space.
+ int Waste() { return accounting_stats_.Waste(); }
+
+ // Returns the address of the first object in this space.
+ Address bottom() { return first_page_->ObjectAreaStart(); }
+
+ // Returns the allocation pointer in this space.
+ Address top() { return allocation_info_.top; }
+
+ // Allocate the requested number of bytes in the space if possible, return a
+ // failure object if not.
+ inline Object* AllocateRaw(int size_in_bytes);
+
+ // Allocate the requested number of bytes for relocation during mark-compact
+ // collection.
+ inline Object* MCAllocateRaw(int size_in_bytes);
+
+
+ // ---------------------------------------------------------------------------
+ // Mark-compact collection support functions
+
+ // Set the relocation point to the beginning of the space.
+ void MCResetRelocationInfo();
+
+ // Writes relocation info to the top page.
+ void MCWriteRelocationInfoToPage() {
+ TopPageOf(mc_forwarding_info_)->mc_relocation_top = mc_forwarding_info_.top;
+ }
+
+ // Computes the offset of a given address in this space to the beginning
+ // of the space.
+ int MCSpaceOffsetForAddress(Address addr);
+
+ // Updates the allocation pointer to the relocation top after a mark-compact
+ // collection.
+ virtual void MCCommitRelocationInfo() = 0;
+
+ // Releases half of unused pages.
+ void Shrink();
+
+ // Ensures that the capacity is at least 'capacity'. Returns false on failure.
+ bool EnsureCapacity(int capacity);
+
+#ifdef ENABLE_HEAP_PROTECTION
+ // Protect/unprotect the space by marking it read-only/writable.
+ void Protect();
+ void Unprotect();
+#endif
+
+#ifdef DEBUG
+ // Print meta info and objects in this space.
+ virtual void Print();
+
+ // Report code object related statistics
+ void CollectCodeStatistics();
+ static void ReportCodeStatistics();
+ static void ResetCodeStatistics();
+#endif
+
+ protected:
+ // Maximum capacity of this space.
+ int max_capacity_;
+
+ // Accounting information for this space.
+ AllocationStats accounting_stats_;
+
+ // The first page in this space.
+ Page* first_page_;
+
+ // Normal allocation information.
+ AllocationInfo allocation_info_;
+
+ // Relocation information during mark-compact collections.
+ AllocationInfo mc_forwarding_info_;
+
+ // Sets allocation pointer to a page bottom.
+ static void SetAllocationInfo(AllocationInfo* alloc_info, Page* p);
+
+ // Returns the top page specified by an allocation info structure.
+ static Page* TopPageOf(AllocationInfo alloc_info) {
+ return Page::FromAllocationTop(alloc_info.limit);
+ }
+
+ // Expands the space by allocating a fixed number of pages. Returns false if
+ // it cannot allocate requested number of pages from OS. Newly allocated
+ // pages are append to the last_page;
+ bool Expand(Page* last_page);
+
+ // Generic fast case allocation function that tries linear allocation in
+ // the top page of 'alloc_info'. Returns NULL on failure.
+ inline HeapObject* AllocateLinearly(AllocationInfo* alloc_info,
+ int size_in_bytes);
+
+ // During normal allocation or deserialization, roll to the next page in
+ // the space (there is assumed to be one) and allocate there. This
+ // function is space-dependent.
+ virtual HeapObject* AllocateInNextPage(Page* current_page,
+ int size_in_bytes) = 0;
+
+ // Slow path of AllocateRaw. This function is space-dependent.
+ virtual HeapObject* SlowAllocateRaw(int size_in_bytes) = 0;
+
+ // Slow path of MCAllocateRaw.
+ HeapObject* SlowMCAllocateRaw(int size_in_bytes);
+
+#ifdef DEBUG
+ void DoPrintRSet(const char* space_name);
+#endif
+ private:
+ // Returns the page of the allocation pointer.
+ Page* AllocationTopPage() { return TopPageOf(allocation_info_); }
+
+ // Returns a pointer to the page of the relocation pointer.
+ Page* MCRelocationTopPage() { return TopPageOf(mc_forwarding_info_); }
+
+#ifdef DEBUG
+ // Returns the number of total pages in this space.
+ int CountTotalPages();
+#endif
+
+ friend class PageIterator;
+};
+
+
+#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
+// HistogramInfo class for recording a single "bar" of a histogram. This
+// class is used for collecting statistics to print to stdout (when compiled
+// with DEBUG) or to the log file (when compiled with
+// ENABLE_LOGGING_AND_PROFILING).
+class HistogramInfo BASE_EMBEDDED {
+ public:
+ HistogramInfo() : number_(0), bytes_(0) {}
+
+ const char* name() { return name_; }
+ void set_name(const char* name) { name_ = name; }
+
+ int number() { return number_; }
+ void increment_number(int num) { number_ += num; }
+
+ int bytes() { return bytes_; }
+ void increment_bytes(int size) { bytes_ += size; }
+
+ // Clear the number of objects and size fields, but not the name.
+ void clear() {
+ number_ = 0;
+ bytes_ = 0;
+ }
+
+ private:
+ const char* name_;
+ int number_;
+ int bytes_;
+};
+#endif
+
+
+// -----------------------------------------------------------------------------
+// SemiSpace in young generation
+//
+// A semispace is a contiguous chunk of memory. The mark-compact collector
+// uses the memory in the from space as a marking stack when tracing live
+// objects.
+
+class SemiSpace : public Space {
+ public:
+ // Constructor.
+ SemiSpace() :Space(NEW_SPACE, NOT_EXECUTABLE) {
+ start_ = NULL;
+ age_mark_ = NULL;
+ }
+
+ // Sets up the semispace using the given chunk.
+ bool Setup(Address start, int initial_capacity, int maximum_capacity);
+
+ // Tear down the space. Heap memory was not allocated by the space, so it
+ // is not deallocated here.
+ void TearDown();
+
+ // True if the space has been set up but not torn down.
+ bool HasBeenSetup() { return start_ != NULL; }
+
+ // Double the size of the semispace by committing extra virtual memory.
+ // Assumes that the caller has checked that the semispace has not reached
+ // its maximum capacity (and thus there is space available in the reserved
+ // address range to grow).
+ bool Double();
+
+ // Returns the start address of the space.
+ Address low() { return start_; }
+ // Returns one past the end address of the space.
+ Address high() { return low() + capacity_; }
+
+ // Age mark accessors.
+ Address age_mark() { return age_mark_; }
+ void set_age_mark(Address mark) { age_mark_ = mark; }
+
+ // True if the address is in the address range of this semispace (not
+ // necessarily below the allocation pointer).
+ bool Contains(Address a) {
+ return (reinterpret_cast<uint32_t>(a) & address_mask_)
+ == reinterpret_cast<uint32_t>(start_);
+ }
+
+ // True if the object is a heap object in the address range of this
+ // semispace (not necessarily below the allocation pointer).
+ bool Contains(Object* o) {
+ return (reinterpret_cast<uint32_t>(o) & object_mask_) == object_expected_;
+ }
+
+ // The offset of an address from the beginning of the space.
+ int SpaceOffsetForAddress(Address addr) { return addr - low(); }
+
+ // If we don't have this here then SemiSpace will be abstract. However
+ // it should never be called.
+ virtual int Size() {
+ UNREACHABLE();
+ return 0;
+ }
+
+#ifdef DEBUG
+ virtual void Print();
+ virtual void Verify();
+#endif
+
+ private:
+ // The current and maximum capacity of the space.
+ int capacity_;
+ int maximum_capacity_;
+
+ // The start address of the space.
+ Address start_;
+ // Used to govern object promotion during mark-compact collection.
+ Address age_mark_;
+
+ // Masks and comparison values to test for containment in this semispace.
+ uint32_t address_mask_;
+ uint32_t object_mask_;
+ uint32_t object_expected_;
+
+ public:
+ TRACK_MEMORY("SemiSpace")
+};
+
+
+// A SemiSpaceIterator 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 SemiSpaceIterator : public ObjectIterator {
+ public:
+ // Create an iterator over the objects in the given space. If no start
+ // address is given, the iterator starts from the bottom of the space. If
+ // no size function is given, the iterator calls Object::Size().
+ explicit SemiSpaceIterator(NewSpace* space);
+ SemiSpaceIterator(NewSpace* space, HeapObjectCallback size_func);
+ SemiSpaceIterator(NewSpace* space, Address start);
+
+ bool has_next() {return current_ < limit_; }
+
+ HeapObject* next() {
+ ASSERT(has_next());
+
+ HeapObject* object = HeapObject::FromAddress(current_);
+ int size = (size_func_ == NULL) ? object->Size() : size_func_(object);
+ ASSERT_OBJECT_SIZE(size);
+
+ current_ += size;
+ return object;
+ }
+
+ // Implementation of the ObjectIterator functions.
+ virtual bool has_next_object() { return has_next(); }
+ virtual HeapObject* next_object() { return next(); }
+
+ private:
+ void Initialize(NewSpace* space, Address start, Address end,
+ HeapObjectCallback size_func);
+
+ // The semispace.
+ SemiSpace* space_;
+ // The current iteration point.
+ Address current_;
+ // The end of iteration.
+ Address limit_;
+ // The callback function.
+ HeapObjectCallback size_func_;
+};
+
+
+// -----------------------------------------------------------------------------
+// The young generation space.
+//
+// The new space consists of a contiguous pair of semispaces. It simply
+// forwards most functions to the appropriate semispace.
+
+class NewSpace : public Space {
+ public:
+ // Constructor.
+ NewSpace() : Space(NEW_SPACE, NOT_EXECUTABLE) {}
+
+ // Sets up the new space using the given chunk.
+ bool Setup(Address start, int size);
+
+ // Tears down the space. Heap memory was not allocated by the space, so it
+ // is not deallocated here.
+ void TearDown();
+
+ // True if the space has been set up but not torn down.
+ bool HasBeenSetup() {
+ return to_space_.HasBeenSetup() && from_space_.HasBeenSetup();
+ }
+
+ // Flip the pair of spaces.
+ void Flip();
+
+ // Doubles the capacity of the semispaces. Assumes that they are not at
+ // their maximum capacity. Returns a flag indicating success or failure.
+ bool Double();
+
+ // True if the address or object lies in the address range of either
+ // semispace (not necessarily below the allocation pointer).
+ bool Contains(Address a) {
+ return (reinterpret_cast<uint32_t>(a) & address_mask_)
+ == reinterpret_cast<uint32_t>(start_);
+ }
+ bool Contains(Object* o) {
+ return (reinterpret_cast<uint32_t>(o) & object_mask_) == object_expected_;
+ }
+
+ // Return the allocated bytes in the active semispace.
+ virtual int Size() { return top() - bottom(); }
+ // Return the current capacity of a semispace.
+ int Capacity() { return capacity_; }
+ // Return the available bytes without growing in the active semispace.
+ int Available() { return Capacity() - Size(); }
+
+ // Return the maximum capacity of a semispace.
+ int MaximumCapacity() { return maximum_capacity_; }
+
+ // Return the address of the allocation pointer in the active semispace.
+ Address top() { return allocation_info_.top; }
+ // Return the address of the first object in the active semispace.
+ Address bottom() { return to_space_.low(); }
+
+ // 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); }
+
+ // The start address of the space and a bit mask. Anding an address in the
+ // new space with the mask will result in the start address.
+ Address start() { return start_; }
+ uint32_t mask() { return address_mask_; }
+
+ // The allocation top and limit addresses.
+ Address* allocation_top_address() { return &allocation_info_.top; }
+ Address* allocation_limit_address() { return &allocation_info_.limit; }
+
+ Object* AllocateRaw(int size_in_bytes) {
+ return AllocateRawInternal(size_in_bytes, &allocation_info_);
+ }
+
+ // Allocate the requested number of bytes for relocation during mark-compact
+ // collection.
+ Object* MCAllocateRaw(int size_in_bytes) {
+ return AllocateRawInternal(size_in_bytes, &mc_forwarding_info_);
+ }
+
+ // Reset the allocation pointer to the beginning of the active semispace.
+ void ResetAllocationInfo();
+ // Reset the reloction pointer to the bottom of the inactive semispace in
+ // preparation for mark-compact collection.
+ void MCResetRelocationInfo();
+ // Update the allocation pointer in the active semispace after a
+ // mark-compact collection.
+ void MCCommitRelocationInfo();
+
+ // Get the extent of the inactive semispace (for use as a marking stack).
+ Address FromSpaceLow() { return from_space_.low(); }
+ Address FromSpaceHigh() { return from_space_.high(); }
+
+ // Get the extent of the active semispace (to sweep newly copied objects
+ // during a scavenge collection).
+ Address ToSpaceLow() { return to_space_.low(); }
+ Address ToSpaceHigh() { return to_space_.high(); }
+
+ // Offsets from the beginning of the semispaces.
+ int ToSpaceOffsetForAddress(Address a) {
+ return to_space_.SpaceOffsetForAddress(a);
+ }
+ int FromSpaceOffsetForAddress(Address a) {
+ return from_space_.SpaceOffsetForAddress(a);
+ }
+
+ // True if the object is a heap object in the address range of the
+ // respective semispace (not necessarily below the allocation pointer of the
+ // semispace).
+ bool ToSpaceContains(Object* o) { return to_space_.Contains(o); }
+ bool FromSpaceContains(Object* o) { return from_space_.Contains(o); }
+
+ bool ToSpaceContains(Address a) { return to_space_.Contains(a); }
+ bool FromSpaceContains(Address a) { return from_space_.Contains(a); }
+
+#ifdef ENABLE_HEAP_PROTECTION
+ // Protect/unprotect the space by marking it read-only/writable.
+ virtual void Protect();
+ virtual void Unprotect();
+#endif
+
+#ifdef DEBUG
+ // Verify the active semispace.
+ virtual void Verify();
+ // Print the active semispace.
+ virtual void Print() { to_space_.Print(); }
+#endif
+
+#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
+ // Iterates the active semispace to collect statistics.
+ void CollectStatistics();
+ // Reports previously collected statistics of the active semispace.
+ void ReportStatistics();
+ // Clears previously collected statistics.
+ void ClearHistograms();
+
+ // Record the allocation or promotion of a heap object. Note that we don't
+ // record every single allocation, but only those that happen in the
+ // to space during a scavenge GC.
+ void RecordAllocation(HeapObject* obj);
+ void RecordPromotion(HeapObject* obj);
+#endif
+
+ private:
+ // The current and maximum capacities of a semispace.
+ int capacity_;
+ int maximum_capacity_;
+
+ // The semispaces.
+ SemiSpace to_space_;
+ SemiSpace from_space_;
+
+ // Start address and bit mask for containment testing.
+ Address start_;
+ uint32_t address_mask_;
+ uint32_t object_mask_;
+ uint32_t object_expected_;
+
+ // Allocation pointer and limit for normal allocation and allocation during
+ // mark-compact collection.
+ AllocationInfo allocation_info_;
+ AllocationInfo mc_forwarding_info_;
+
+#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
+ HistogramInfo* allocated_histogram_;
+ HistogramInfo* promoted_histogram_;
+#endif
+
+ // Implementation of AllocateRaw and MCAllocateRaw.
+ inline Object* AllocateRawInternal(int size_in_bytes,
+ AllocationInfo* alloc_info);
+
+ friend class SemiSpaceIterator;
+
+ public:
+ TRACK_MEMORY("NewSpace")
+};
+
+
+// -----------------------------------------------------------------------------
+// Free lists for old object spaces
+//
+// Free-list nodes are free blocks in the heap. They look like heap objects
+// (free-list node pointers have the heap object tag, and they have a map like
+// a heap object). They have a size and a next pointer. The next pointer is
+// the raw address of the next free list node (or NULL).
+class FreeListNode: public HeapObject {
+ public:
+ // Obtain a free-list node from a raw address. This is not a cast because
+ // it does not check nor require that the first word at the address is a map
+ // pointer.
+ static FreeListNode* FromAddress(Address address) {
+ return reinterpret_cast<FreeListNode*>(HeapObject::FromAddress(address));
+ }
+
+ // Set the size in bytes, which can be read with HeapObject::Size(). This
+ // function also writes a map to the first word of the block so that it
+ // looks like a heap object to the garbage collector and heap iteration
+ // functions.
+ void set_size(int size_in_bytes);
+
+ // Accessors for the next field.
+ inline Address next();
+ inline void set_next(Address next);
+
+ private:
+ static const int kNextOffset = Array::kHeaderSize;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FreeListNode);
+};
+
+
+// The free list for the old space.
+class OldSpaceFreeList BASE_EMBEDDED {
+ public:
+ explicit OldSpaceFreeList(AllocationSpace owner);
+
+ // Clear the free list.
+ void Reset();
+
+ // Return the number of bytes available on the free list.
+ int available() { return available_; }
+
+ // Place 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 have been lost due to internal fragmentation by
+ // freeing the block. Bookkeeping information will be written to the block,
+ // ie, 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);
+
+ // 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
+ // number of bytes lost to fragmentation is returned in the output parameter
+ // 'wasted_bytes'. The size should be a non-zero multiple of the word size.
+ Object* Allocate(int size_in_bytes, int* wasted_bytes);
+
+ private:
+ // The size range of blocks, in bytes. (Smaller allocations are allowed, but
+ // will always result in waste.)
+ static const int kMinBlockSize = Array::kHeaderSize + kPointerSize;
+ static const int kMaxBlockSize = Page::kMaxHeapObjectSize;
+
+ // The identity of the owning space, for building allocation Failure
+ // objects.
+ AllocationSpace owner_;
+
+ // Total available bytes in all blocks on this free list.
+ int available_;
+
+ // Blocks are put on exact free lists in an array, indexed by size in words.
+ // The available sizes are kept in an increasingly ordered list. Entries
+ // corresponding to sizes < kMinBlockSize always have an empty free list
+ // (but index kHead is used for the head of the size list).
+ struct SizeNode {
+ // Address of the head FreeListNode of the implied block size or NULL.
+ Address head_node_;
+ // Size (words) of the next larger available size if head_node_ != NULL.
+ int next_size_;
+ };
+ static const int kFreeListsLength = kMaxBlockSize / kPointerSize + 1;
+ SizeNode free_[kFreeListsLength];
+
+ // Sentinel elements for the size list. Real elements are in ]kHead..kEnd[.
+ static const int kHead = kMinBlockSize / kPointerSize - 1;
+ static const int kEnd = kMaxInt;
+
+ // We keep a "finger" in the size list to speed up a common pattern:
+ // repeated requests for the same or increasing sizes.
+ int finger_;
+
+ // Starting from *prev, find and return the smallest size >= index (words),
+ // or kEnd. Update *prev to be the largest size < index, or kHead.
+ int FindSize(int index, int* prev) {
+ int cur = free_[*prev].next_size_;
+ while (cur < index) {
+ *prev = cur;
+ cur = free_[cur].next_size_;
+ }
+ return cur;
+ }
+
+ // Remove an existing element from the size list.
+ void RemoveSize(int index) {
+ int prev = kHead;
+ int cur = FindSize(index, &prev);
+ ASSERT(cur == index);
+ free_[prev].next_size_ = free_[cur].next_size_;
+ finger_ = prev;
+ }
+
+ // Insert a new element into the size list.
+ void InsertSize(int index) {
+ int prev = kHead;
+ int cur = FindSize(index, &prev);
+ ASSERT(cur != index);
+ free_[prev].next_size_ = index;
+ free_[index].next_size_ = cur;
+ }
+
+ // The size list is not updated during a sequence of calls to Free, but is
+ // rebuilt before the next allocation.
+ void RebuildSizeList();
+ bool needs_rebuild_;
+
+#ifdef DEBUG
+ // Does this free list contain a free block located at the address of 'node'?
+ bool Contains(FreeListNode* node);
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(OldSpaceFreeList);
+};
+
+
+// The free list for the map space.
+class MapSpaceFreeList BASE_EMBEDDED {
+ public:
+ explicit MapSpaceFreeList(AllocationSpace owner);
+
+ // Clear the free list.
+ void Reset();
+
+ // Return the number of bytes available on the free list.
+ int available() { return available_; }
+
+ // Place a node on the free list. The block starting at 'start' (assumed to
+ // have size Map::kSize) is placed on the free list. Bookkeeping
+ // information will be written to the block, ie, its contents will be
+ // destroyed. The start address should be word aligned.
+ void Free(Address start);
+
+ // Allocate a map-sized block from the free list. The block is unitialized.
+ // A failure is returned if no block is available.
+ Object* Allocate();
+
+ private:
+ // Available bytes on the free list.
+ int available_;
+
+ // The head of the free list.
+ Address head_;
+
+ // The identity of the owning space, for building allocation Failure
+ // objects.
+ AllocationSpace owner_;
+
+ DISALLOW_COPY_AND_ASSIGN(MapSpaceFreeList);
+};
+
+
+// -----------------------------------------------------------------------------
+// Old object space (excluding map objects)
+
+class OldSpace : public PagedSpace {
+ public:
+ // Creates an old space object with a given maximum capacity.
+ // The constructor does not allocate pages from OS.
+ explicit OldSpace(int max_capacity,
+ AllocationSpace id,
+ Executability executable)
+ : PagedSpace(max_capacity, id, executable), free_list_(id) {
+ }
+
+ // The bytes available on the free list (ie, not above the linear allocation
+ // pointer).
+ int AvailableFree() { return free_list_.available(); }
+
+ // The top of allocation in a page in this space. Undefined if page is unused.
+ virtual Address PageAllocationTop(Page* page) {
+ return page == TopPageOf(allocation_info_) ? top() : page->ObjectAreaEnd();
+ }
+
+ // Give a block of memory to the space's free list. It might be added to
+ // the free list or accounted as waste.
+ void Free(Address start, int size_in_bytes) {
+ int wasted_bytes = free_list_.Free(start, size_in_bytes);
+ accounting_stats_.DeallocateBytes(size_in_bytes);
+ accounting_stats_.WasteBytes(wasted_bytes);
+ }
+
+ // Prepare for full garbage collection. Resets the relocation pointer and
+ // clears the free list.
+ virtual void PrepareForMarkCompact(bool will_compact);
+
+ // Adjust the top of relocation pointer to point to the end of the object
+ // given by 'address' and 'size_in_bytes'. Move it to the next page if
+ // necessary, ensure that it points to the address, then increment it by the
+ // size.
+ void MCAdjustRelocationEnd(Address address, int size_in_bytes);
+
+ // Updates the allocation pointer to the relocation top after a mark-compact
+ // collection.
+ virtual void MCCommitRelocationInfo();
+
+#ifdef DEBUG
+ // Verify integrity of this space.
+ virtual void Verify();
+
+ // Reports statistics for the space
+ void ReportStatistics();
+ // Dump the remembered sets in the space to stdout.
+ void PrintRSet();
+#endif
+
+ protected:
+ // Virtual function in the superclass. Slow path of AllocateRaw.
+ HeapObject* SlowAllocateRaw(int size_in_bytes);
+
+ // Virtual function in the superclass. Allocate linearly at the start of
+ // the page after current_page (there is assumed to be one).
+ HeapObject* AllocateInNextPage(Page* current_page, int size_in_bytes);
+
+ private:
+ // The space's free list.
+ OldSpaceFreeList free_list_;
+
+ // During relocation, we keep a pointer to the most recently relocated
+ // object in order to know when to move to the next page.
+ Address mc_end_of_relocation_;
+
+ public:
+ TRACK_MEMORY("OldSpace")
+};
+
+
+// -----------------------------------------------------------------------------
+// Old space for all map objects
+
+class MapSpace : public PagedSpace {
+ public:
+ // Creates a map space object with a maximum capacity.
+ explicit MapSpace(int max_capacity, AllocationSpace id)
+ : PagedSpace(max_capacity, id, NOT_EXECUTABLE), free_list_(id) { }
+
+ // The top of allocation in a page in this space. Undefined if page is unused.
+ virtual Address PageAllocationTop(Page* page) {
+ return page == TopPageOf(allocation_info_) ? top()
+ : page->ObjectAreaEnd() - kPageExtra;
+ }
+
+ // Give a map-sized block of memory to the space's free list.
+ void Free(Address start) {
+ free_list_.Free(start);
+ accounting_stats_.DeallocateBytes(Map::kSize);
+ }
+
+ // Given an index, returns the page address.
+ Address PageAddress(int page_index) { return page_addresses_[page_index]; }
+
+ // Prepares for a mark-compact GC.
+ virtual void PrepareForMarkCompact(bool will_compact);
+
+ // Updates the allocation pointer to the relocation top after a mark-compact
+ // collection.
+ virtual void MCCommitRelocationInfo();
+
+#ifdef DEBUG
+ // Verify integrity of this space.
+ virtual void Verify();
+
+ // Reports statistic info of the space
+ void ReportStatistics();
+ // Dump the remembered sets in the space to stdout.
+ void PrintRSet();
+#endif
+
+ // Constants.
+ static const int kMapPageIndexBits = 10;
+ static const int kMaxMapPageIndex = (1 << kMapPageIndexBits) - 1;
+
+ static const int kPageExtra = Page::kObjectAreaSize % Map::kSize;
+
+ protected:
+ // Virtual function in the superclass. Slow path of AllocateRaw.
+ HeapObject* SlowAllocateRaw(int size_in_bytes);
+
+ // Virtual function in the superclass. Allocate linearly at the start of
+ // the page after current_page (there is assumed to be one).
+ HeapObject* AllocateInNextPage(Page* current_page, int size_in_bytes);
+
+ private:
+ // The space's free list.
+ MapSpaceFreeList free_list_;
+
+ // An array of page start address in a map space.
+ Address page_addresses_[kMaxMapPageIndex + 1];
+
+ public:
+ TRACK_MEMORY("MapSpace")
+};
+
+
+// -----------------------------------------------------------------------------
+// Large objects ( > Page::kMaxHeapObjectSize ) are allocated and managed by
+// the large object space. A large object is allocated from OS heap with
+// extra padding bytes (Page::kPageSize + Page::kObjectStartOffset).
+// A large object always starts at Page::kObjectStartOffset to a page.
+// Large objects do not move during garbage collections.
+
+// A LargeObjectChunk holds exactly one large object page with exactly one
+// large object.
+class LargeObjectChunk {
+ public:
+ // Allocates a new LargeObjectChunk that contains a large object page
+ // (Page::kPageSize aligned) that has at least size_in_bytes (for a large
+ // object and possibly extra remembered set words) bytes after the object
+ // area start of that page. The allocated chunk size is set in the output
+ // parameter chunk_size.
+ static LargeObjectChunk* New(int size_in_bytes,
+ size_t* chunk_size,
+ Executability executable);
+
+ // Interpret a raw address as a large object chunk.
+ static LargeObjectChunk* FromAddress(Address address) {
+ return reinterpret_cast<LargeObjectChunk*>(address);
+ }
+
+ // Returns the address of this chunk.
+ Address address() { return reinterpret_cast<Address>(this); }
+
+ // Accessors for the fields of the chunk.
+ LargeObjectChunk* next() { return next_; }
+ void set_next(LargeObjectChunk* chunk) { next_ = chunk; }
+
+ size_t size() { return size_; }
+ void set_size(size_t size_in_bytes) { size_ = size_in_bytes; }
+
+ // Returns the object in this chunk.
+ inline HeapObject* GetObject();
+
+ // Given a requested size (including any extra remembered set words),
+ // returns the physical size of a chunk to be allocated.
+ static int ChunkSizeFor(int size_in_bytes);
+
+ // Given a chunk size, returns the object size it can accommodate (not
+ // including any extra remembered set words). Used by
+ // LargeObjectSpace::Available. Note that this can overestimate the size
+ // of object that will fit in a chunk---if the object requires extra
+ // remembered set words (eg, for large fixed arrays), the actual object
+ // size for the chunk will be smaller than reported by this function.
+ static int ObjectSizeFor(int chunk_size) {
+ if (chunk_size <= (Page::kPageSize + Page::kObjectStartOffset)) return 0;
+ return chunk_size - Page::kPageSize - Page::kObjectStartOffset;
+ }
+
+ private:
+ // A pointer to the next large object chunk in the space or NULL.
+ LargeObjectChunk* next_;
+
+ // The size of this chunk.
+ size_t size_;
+
+ public:
+ TRACK_MEMORY("LargeObjectChunk")
+};
+
+
+class LargeObjectSpace : public Space {
+ public:
+ explicit LargeObjectSpace(AllocationSpace id);
+ virtual ~LargeObjectSpace() {}
+
+ // Initializes internal data structures.
+ bool Setup();
+
+ // Releases internal resources, frees objects in this space.
+ void TearDown();
+
+ // Allocates a (non-FixedArray, non-Code) large object.
+ Object* AllocateRaw(int size_in_bytes);
+ // Allocates a large Code object.
+ Object* AllocateRawCode(int size_in_bytes);
+ // Allocates a large FixedArray.
+ Object* AllocateRawFixedArray(int size_in_bytes);
+
+ // Available bytes for objects in this space, not including any extra
+ // remembered set words.
+ int Available() {
+ return LargeObjectChunk::ObjectSizeFor(MemoryAllocator::Available());
+ }
+
+ virtual int Size() {
+ return size_;
+ }
+
+ int PageCount() {
+ return page_count_;
+ }
+
+ // Finds an object for a given address, returns Failure::Exception()
+ // if it is not found. The function iterates through all objects in this
+ // space, may be slow.
+ Object* FindObject(Address a);
+
+ // Clears remembered sets.
+ void ClearRSet();
+
+ // Iterates objects whose remembered set bits are set.
+ void IterateRSet(ObjectSlotCallback func);
+
+ // Frees unmarked objects.
+ void FreeUnmarkedObjects();
+
+ // Checks whether a heap object is in this space; O(1).
+ bool Contains(HeapObject* obj);
+
+ // Checks whether the space is empty.
+ bool IsEmpty() { return first_chunk_ == NULL; }
+
+#ifdef ENABLE_HEAP_PROTECTION
+ // Protect/unprotect the space by marking it read-only/writable.
+ void Protect();
+ void Unprotect();
+#endif
+
+#ifdef DEBUG
+ virtual void Verify();
+ virtual void Print();
+ void ReportStatistics();
+ void CollectCodeStatistics();
+ // Dump the remembered sets in the space to stdout.
+ void PrintRSet();
+#endif
+ // Checks whether an address is in the object area in this space. It
+ // iterates all objects in the space. May be slow.
+ bool SlowContains(Address addr) { return !FindObject(addr)->IsFailure(); }
+
+ private:
+ // The head of the linked list of large object chunks.
+ LargeObjectChunk* first_chunk_;
+ int size_; // allocated bytes
+ int page_count_; // number of chunks
+
+
+ // Shared implementation of AllocateRaw, AllocateRawCode and
+ // AllocateRawFixedArray.
+ Object* AllocateRawInternal(int requested_size,
+ int object_size,
+ Executability executable);
+
+ // Returns the number of extra bytes (rounded up to the nearest full word)
+ // required for extra_object_bytes of extra pointers (in bytes).
+ static inline int ExtraRSetBytesFor(int extra_object_bytes);
+
+ friend class LargeObjectIterator;
+
+ public:
+ TRACK_MEMORY("LargeObjectSpace")
+};
+
+
+class LargeObjectIterator: public ObjectIterator {
+ public:
+ explicit LargeObjectIterator(LargeObjectSpace* space);
+ LargeObjectIterator(LargeObjectSpace* space, HeapObjectCallback size_func);
+
+ bool has_next() { return current_ != NULL; }
+ HeapObject* next();
+
+ // implementation of ObjectIterator.
+ virtual bool has_next_object() { return has_next(); }
+ virtual HeapObject* next_object() { return next(); }
+
+ private:
+ LargeObjectChunk* current_;
+ HeapObjectCallback size_func_;
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_SPACES_H_