Index: Source/platform/heap/Heap.h |
diff --git a/Source/platform/heap/Heap.h b/Source/platform/heap/Heap.h |
index 18a435a46a32c09576781621e5ec7675ae21a9aa..d3b4b4d9519c3b7f1e35ff32a2b34ec79e72dec7 100644 |
--- a/Source/platform/heap/Heap.h |
+++ b/Source/platform/heap/Heap.h |
@@ -33,794 +33,16 @@ |
#include "platform/PlatformExport.h" |
#include "platform/heap/GCInfo.h" |
+#include "platform/heap/HeapPage.h" |
#include "platform/heap/ThreadState.h" |
#include "platform/heap/Visitor.h" |
-#include "public/platform/WebThread.h" |
#include "wtf/AddressSanitizer.h" |
#include "wtf/Assertions.h" |
#include "wtf/Atomics.h" |
-#include "wtf/ContainerAnnotations.h" |
#include "wtf/Forward.h" |
-#include "wtf/PageAllocator.h" |
-#include <stdint.h> |
namespace blink { |
-const size_t blinkPageSizeLog2 = 17; |
-const size_t blinkPageSize = 1 << blinkPageSizeLog2; |
-const size_t blinkPageOffsetMask = blinkPageSize - 1; |
-const size_t blinkPageBaseMask = ~blinkPageOffsetMask; |
- |
-// We allocate pages at random addresses but in groups of |
-// blinkPagesPerRegion at a given random address. We group pages to |
-// not spread out too much over the address space which would blow |
-// away the page tables and lead to bad performance. |
-const size_t blinkPagesPerRegion = 10; |
- |
-// TODO(nya): Replace this with something like #if ENABLE_NACL. |
-#if 0 |
-// NaCl's system page size is 64 KB. This causes a problem in Oilpan's heap |
-// layout because Oilpan allocates two guard pages for each blink page |
-// (whose size is 128 KB). So we don't use guard pages in NaCl. |
-const size_t blinkGuardPageSize = 0; |
-#else |
-const size_t blinkGuardPageSize = WTF::kSystemPageSize; |
-#endif |
- |
-// Double precision floats are more efficient when 8 byte aligned, so we 8 byte |
-// align all allocations even on 32 bit. |
-const size_t allocationGranularity = 8; |
-const size_t allocationMask = allocationGranularity - 1; |
-const size_t objectStartBitMapSize = (blinkPageSize + ((8 * allocationGranularity) - 1)) / (8 * allocationGranularity); |
-const size_t reservedForObjectBitMap = ((objectStartBitMapSize + allocationMask) & ~allocationMask); |
-const size_t maxHeapObjectSizeLog2 = 27; |
-const size_t maxHeapObjectSize = 1 << maxHeapObjectSizeLog2; |
-const size_t largeObjectSizeThreshold = blinkPageSize / 2; |
- |
-// A zap value used for freed memory that is allowed to be added to the free |
-// list in the next addToFreeList(). |
-const uint8_t reuseAllowedZapValue = 0x2a; |
-// A zap value used for freed memory that is forbidden to be added to the free |
-// list in the next addToFreeList(). |
-const uint8_t reuseForbiddenZapValue = 0x2c; |
- |
-// In non-production builds, memory is zapped when it's freed. The zapped |
-// memory is zeroed out when the memory is reused in Heap::allocateObject(). |
-// In production builds, memory is not zapped (for performance). The memory |
-// is just zeroed out when it is added to the free list. |
-#if defined(MEMORY_SANITIZER) |
-// TODO(kojii): We actually need __msan_poison/unpoison here, but it'll be |
-// added later. |
-#define SET_MEMORY_INACCESSIBLE(address, size) \ |
- FreeList::zapFreedMemory(address, size); |
-#define SET_MEMORY_ACCESSIBLE(address, size) \ |
- memset((address), 0, (size)) |
-#elif ENABLE(ASSERT) || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER) |
-#define SET_MEMORY_INACCESSIBLE(address, size) \ |
- FreeList::zapFreedMemory(address, size); \ |
- ASAN_POISON_MEMORY_REGION(address, size) |
-#define SET_MEMORY_ACCESSIBLE(address, size) \ |
- ASAN_UNPOISON_MEMORY_REGION(address, size); \ |
- memset((address), 0, (size)) |
-#else |
-#define SET_MEMORY_INACCESSIBLE(address, size) memset((address), 0, (size)) |
-#define SET_MEMORY_ACCESSIBLE(address, size) do { } while (false) |
-#endif |
- |
-class CallbackStack; |
-class FreePagePool; |
-class NormalPageHeap; |
-class OrphanedPagePool; |
-class PageMemory; |
-class PageMemoryRegion; |
-class WebProcessMemoryDump; |
- |
-#if ENABLE(GC_PROFILING) |
-class TracedValue; |
-#endif |
- |
-// HeapObjectHeader is 4 byte (32 bit) that has the following layout: |
-// |
-// | gcInfoIndex (14 bit) | DOM mark bit (1 bit) | size (14 bit) | dead bit (1 bit) | freed bit (1 bit) | mark bit (1 bit) | |
-// |
-// - For non-large objects, 14 bit is enough for |size| because the blink |
-// page size is 2^17 byte and each object is guaranteed to be aligned with |
-// 2^3 byte. |
-// - For large objects, |size| is 0. The actual size of a large object is |
-// stored in LargeObjectPage::m_payloadSize. |
-// - 1 bit used to mark DOM trees for V8. |
-// - 14 bit is enough for gcInfoIndex because there are less than 2^14 types |
-// in Blink. |
-const size_t headerDOMMarkBitMask = 1u << 17; |
-const size_t headerGCInfoIndexShift = 18; |
-const size_t headerGCInfoIndexMask = (static_cast<size_t>((1 << 14) - 1)) << headerGCInfoIndexShift; |
-const size_t headerSizeMask = (static_cast<size_t>((1 << 14) - 1)) << 3; |
-const size_t headerMarkBitMask = 1; |
-const size_t headerFreedBitMask = 2; |
-// The dead bit is used for objects that have gone through a GC marking, but did |
-// not get swept before a new GC started. In that case we set the dead bit on |
-// objects that were not marked in the previous GC to ensure we are not tracing |
-// them via a conservatively found pointer. Tracing dead objects could lead to |
-// tracing of already finalized objects in another thread's heap which is a |
-// use-after-free situation. |
-const size_t headerDeadBitMask = 4; |
-// On free-list entries we reuse the dead bit to distinguish a normal free-list |
-// entry from one that has been promptly freed. |
-const size_t headerPromptlyFreedBitMask = headerFreedBitMask | headerDeadBitMask; |
-const size_t largeObjectSizeInHeader = 0; |
-const size_t gcInfoIndexForFreeListHeader = 0; |
-const size_t nonLargeObjectPageSizeMax = 1 << 17; |
- |
-static_assert(nonLargeObjectPageSizeMax >= blinkPageSize, "max size supported by HeapObjectHeader must at least be blinkPageSize"); |
- |
-class PLATFORM_EXPORT HeapObjectHeader { |
-public: |
- // If gcInfoIndex is 0, this header is interpreted as a free list header. |
- NO_SANITIZE_ADDRESS |
- HeapObjectHeader(size_t size, size_t gcInfoIndex) |
- { |
-#if ENABLE(ASSERT) |
- m_magic = magic; |
-#endif |
-#if ENABLE(GC_PROFILING) |
- m_age = 0; |
-#endif |
- // sizeof(HeapObjectHeader) must be equal to or smaller than |
- // allocationGranurarity, because HeapObjectHeader is used as a header |
- // for an freed entry. Given that the smallest entry size is |
- // allocationGranurarity, HeapObjectHeader must fit into the size. |
- static_assert(sizeof(HeapObjectHeader) <= allocationGranularity, "size of HeapObjectHeader must be smaller than allocationGranularity"); |
-#if CPU(64BIT) |
- static_assert(sizeof(HeapObjectHeader) == 8, "size of HeapObjectHeader must be 8 byte aligned"); |
-#endif |
- |
- ASSERT(gcInfoIndex < GCInfoTable::maxIndex); |
- ASSERT(size < nonLargeObjectPageSizeMax); |
- ASSERT(!(size & allocationMask)); |
- m_encoded = (gcInfoIndex << headerGCInfoIndexShift) | size | (gcInfoIndex ? 0 : headerFreedBitMask); |
- } |
- |
- NO_SANITIZE_ADDRESS |
- bool isFree() const { return m_encoded & headerFreedBitMask; } |
- NO_SANITIZE_ADDRESS |
- bool isPromptlyFreed() const { return (m_encoded & headerPromptlyFreedBitMask) == headerPromptlyFreedBitMask; } |
- NO_SANITIZE_ADDRESS |
- void markPromptlyFreed() { m_encoded |= headerPromptlyFreedBitMask; } |
- size_t size() const; |
- |
- NO_SANITIZE_ADDRESS |
- size_t gcInfoIndex() const { return (m_encoded & headerGCInfoIndexMask) >> headerGCInfoIndexShift; } |
- NO_SANITIZE_ADDRESS |
- void setSize(size_t size) { m_encoded = size | (m_encoded & ~headerSizeMask); } |
- bool isMarked() const; |
- void mark(); |
- void unmark(); |
- void markDead(); |
- bool isDead() const; |
- |
- Address payload(); |
- size_t payloadSize(); |
- Address payloadEnd(); |
- |
-#if ENABLE(ASSERT) |
- bool checkHeader() const; |
- // Zap magic number with a new magic number that means there was once an |
- // object allocated here, but it was freed because nobody marked it during |
- // GC. |
- void zapMagic(); |
-#endif |
- |
- void finalize(Address, size_t); |
- static HeapObjectHeader* fromPayload(const void*); |
- |
- static const uint16_t magic = 0xfff1; |
- static const uint16_t zappedMagic = 0x4321; |
- |
-#if ENABLE(GC_PROFILING) |
- NO_SANITIZE_ADDRESS |
- size_t encodedSize() const { return m_encoded; } |
- |
- NO_SANITIZE_ADDRESS |
- size_t age() const { return m_age; } |
- |
- NO_SANITIZE_ADDRESS |
- void incrementAge() |
- { |
- if (m_age < maxHeapObjectAge) |
- m_age++; |
- } |
-#endif |
- |
-#if !ENABLE(ASSERT) && !ENABLE(GC_PROFILING) && CPU(64BIT) |
- // This method is needed just to avoid compilers from removing m_padding. |
- uint64_t unusedMethod() const { return m_padding; } |
-#endif |
- |
-private: |
- uint32_t m_encoded; |
-#if ENABLE(ASSERT) |
- uint16_t m_magic; |
-#endif |
-#if ENABLE(GC_PROFILING) |
- uint8_t m_age; |
-#endif |
- |
- // In 64 bit architectures, we intentionally add 4 byte padding immediately |
- // after the HeapHeaderObject. This is because: |
- // |
- // | HeapHeaderObject (4 byte) | padding (4 byte) | object payload (8 * n byte) | |
- // ^8 byte aligned ^8 byte aligned |
- // |
- // is better than: |
- // |
- // | HeapHeaderObject (4 byte) | object payload (8 * n byte) | padding (4 byte) | |
- // ^4 byte aligned ^8 byte aligned ^4 byte aligned |
- // |
- // since the former layout aligns both header and payload to 8 byte. |
-#if !ENABLE(ASSERT) && !ENABLE(GC_PROFILING) && CPU(64BIT) |
- uint32_t m_padding; |
-#endif |
-}; |
- |
-class FreeListEntry final : public HeapObjectHeader { |
-public: |
- NO_SANITIZE_ADDRESS |
- explicit FreeListEntry(size_t size) |
- : HeapObjectHeader(size, gcInfoIndexForFreeListHeader) |
- , m_next(nullptr) |
- { |
-#if ENABLE(ASSERT) |
- ASSERT(size >= sizeof(HeapObjectHeader)); |
- zapMagic(); |
-#endif |
- } |
- |
- Address address() { return reinterpret_cast<Address>(this); } |
- |
- NO_SANITIZE_ADDRESS |
- void unlink(FreeListEntry** prevNext) |
- { |
- *prevNext = m_next; |
- m_next = nullptr; |
- } |
- |
- NO_SANITIZE_ADDRESS |
- void link(FreeListEntry** prevNext) |
- { |
- m_next = *prevNext; |
- *prevNext = this; |
- } |
- |
- NO_SANITIZE_ADDRESS |
- FreeListEntry* next() const { return m_next; } |
- |
- NO_SANITIZE_ADDRESS |
- void append(FreeListEntry* next) |
- { |
- ASSERT(!m_next); |
- m_next = next; |
- } |
- |
-private: |
- FreeListEntry* m_next; |
-}; |
- |
-// Blink heap pages are set up with a guard page before and after the payload. |
-inline size_t blinkPagePayloadSize() |
-{ |
- return blinkPageSize - 2 * blinkGuardPageSize; |
-} |
- |
-// Blink heap pages are aligned to the Blink heap page size. |
-// Therefore, the start of a Blink page can be obtained by |
-// rounding down to the Blink page size. |
-inline Address roundToBlinkPageStart(Address address) |
-{ |
- return reinterpret_cast<Address>(reinterpret_cast<uintptr_t>(address) & blinkPageBaseMask); |
-} |
- |
-inline Address roundToBlinkPageEnd(Address address) |
-{ |
- return reinterpret_cast<Address>(reinterpret_cast<uintptr_t>(address - 1) & blinkPageBaseMask) + blinkPageSize; |
-} |
- |
-// Masks an address down to the enclosing blink page base address. |
-inline Address blinkPageAddress(Address address) |
-{ |
- return reinterpret_cast<Address>(reinterpret_cast<uintptr_t>(address) & blinkPageBaseMask); |
-} |
- |
-inline bool vTableInitialized(void* objectPointer) |
-{ |
- return !!(*reinterpret_cast<Address*>(objectPointer)); |
-} |
- |
-#if ENABLE(ASSERT) |
-// Sanity check for a page header address: the address of the page |
-// header should be OS page size away from being Blink page size |
-// aligned. |
-inline bool isPageHeaderAddress(Address address) |
-{ |
- return !((reinterpret_cast<uintptr_t>(address) & blinkPageOffsetMask) - blinkGuardPageSize); |
-} |
-#endif |
- |
-// BasePage is a base class for NormalPage and LargeObjectPage. |
-// |
-// - NormalPage is a page whose size is |blinkPageSize|. NormalPage can contain |
-// multiple objects in the page. An object whose size is smaller than |
-// |largeObjectSizeThreshold| is stored in NormalPage. |
-// |
-// - LargeObjectPage is a page that contains only one object. The object size |
-// is arbitrary. An object whose size is larger than |blinkPageSize| is stored |
-// as a single project in LargeObjectPage. |
-// |
-// Note: An object whose size is between |largeObjectSizeThreshold| and |
-// |blinkPageSize| can go to either of NormalPage or LargeObjectPage. |
-class BasePage { |
-public: |
- BasePage(PageMemory*, BaseHeap*); |
- virtual ~BasePage() { } |
- |
- void link(BasePage** previousNext) |
- { |
- m_next = *previousNext; |
- *previousNext = this; |
- } |
- void unlink(BasePage** previousNext) |
- { |
- *previousNext = m_next; |
- m_next = nullptr; |
- } |
- BasePage* next() const { return m_next; } |
- |
- // virtual methods are slow. So performance-sensitive methods |
- // should be defined as non-virtual methods on NormalPage and LargeObjectPage. |
- // The following methods are not performance-sensitive. |
- virtual size_t objectPayloadSizeForTesting() = 0; |
- virtual bool isEmpty() = 0; |
- virtual void removeFromHeap() = 0; |
- virtual void sweep() = 0; |
- virtual void makeConsistentForGC() = 0; |
- virtual void makeConsistentForMutator() = 0; |
- |
-#if defined(ADDRESS_SANITIZER) |
- virtual void poisonObjects(ThreadState::ObjectsToPoison, ThreadState::Poisoning) = 0; |
-#endif |
- // Check if the given address points to an object in this |
- // heap page. If so, find the start of that object and mark it |
- // using the given Visitor. Otherwise do nothing. The pointer must |
- // be within the same aligned blinkPageSize as the this-pointer. |
- // |
- // This is used during conservative stack scanning to |
- // conservatively mark all objects that could be referenced from |
- // the stack. |
- virtual void checkAndMarkPointer(Visitor*, Address) = 0; |
- virtual void markOrphaned(); |
- |
- virtual void takeSnapshot(String dumpBaseName, size_t pageIndex, ThreadState::GCSnapshotInfo&, size_t* outFreeSize, size_t* outFreeCount) = 0; |
-#if ENABLE(GC_PROFILING) |
- virtual const GCInfo* findGCInfo(Address) = 0; |
- virtual void snapshot(TracedValue*, ThreadState::SnapshotInfo*) = 0; |
- virtual void incrementMarkedObjectsAge() = 0; |
- virtual void countMarkedObjects(ClassAgeCountsMap&) = 0; |
- virtual void countObjectsToSweep(ClassAgeCountsMap&) = 0; |
-#endif |
-#if ENABLE(ASSERT) || ENABLE(GC_PROFILING) |
- virtual bool contains(Address) = 0; |
-#endif |
- virtual size_t size() = 0; |
- virtual bool isLargeObjectPage() { return false; } |
- |
- Address address() { return reinterpret_cast<Address>(this); } |
- PageMemory* storage() const { return m_storage; } |
- BaseHeap* heap() const { return m_heap; } |
- bool orphaned() { return !m_heap; } |
- bool terminating() { return m_terminating; } |
- void setTerminating() { m_terminating = true; } |
- |
- // Returns true if this page has been swept by the ongoing lazy sweep. |
- bool hasBeenSwept() const { return m_swept; } |
- |
- void markAsSwept() |
- { |
- ASSERT(!m_swept); |
- m_swept = true; |
- } |
- |
- void markAsUnswept() |
- { |
- ASSERT(m_swept); |
- m_swept = false; |
- } |
- |
-private: |
- PageMemory* m_storage; |
- BaseHeap* m_heap; |
- BasePage* m_next; |
- // Whether the page is part of a terminating thread or not. |
- bool m_terminating; |
- |
- // Track the sweeping state of a page. Set to true once |
- // the lazy sweep completes has processed it. |
- // |
- // Set to false at the start of a sweep, true upon completion |
- // of lazy sweeping. |
- bool m_swept; |
- friend class BaseHeap; |
-}; |
- |
-class NormalPage final : public BasePage { |
-public: |
- NormalPage(PageMemory*, BaseHeap*); |
- |
- Address payload() |
- { |
- return address() + pageHeaderSize(); |
- } |
- size_t payloadSize() |
- { |
- return (blinkPagePayloadSize() - pageHeaderSize()) & ~allocationMask; |
- } |
- Address payloadEnd() { return payload() + payloadSize(); } |
- bool containedInObjectPayload(Address address) |
- { |
- return payload() <= address && address < payloadEnd(); |
- } |
- |
- size_t objectPayloadSizeForTesting() override; |
- bool isEmpty() override; |
- void removeFromHeap() override; |
- void sweep() override; |
- void makeConsistentForGC() override; |
- void makeConsistentForMutator() override; |
-#if defined(ADDRESS_SANITIZER) |
- void poisonObjects(ThreadState::ObjectsToPoison, ThreadState::Poisoning) override; |
-#endif |
- void checkAndMarkPointer(Visitor*, Address) override; |
- void markOrphaned() override; |
- |
- void takeSnapshot(String dumpBaseName, size_t pageIndex, ThreadState::GCSnapshotInfo&, size_t* outFreeSize, size_t* outFreeCount) override; |
-#if ENABLE(GC_PROFILING) |
- const GCInfo* findGCInfo(Address) override; |
- void snapshot(TracedValue*, ThreadState::SnapshotInfo*) override; |
- void incrementMarkedObjectsAge() override; |
- void countMarkedObjects(ClassAgeCountsMap&) override; |
- void countObjectsToSweep(ClassAgeCountsMap&) override; |
-#endif |
-#if ENABLE(ASSERT) || ENABLE(GC_PROFILING) |
- // Returns true for the whole blinkPageSize page that the page is on, even |
- // for the header, and the unmapped guard page at the start. That ensures |
- // the result can be used to populate the negative page cache. |
- bool contains(Address) override; |
-#endif |
- size_t size() override { return blinkPageSize; } |
- static size_t pageHeaderSize() |
- { |
- // Compute the amount of padding we have to add to a header to make |
- // the size of the header plus the padding a multiple of 8 bytes. |
- size_t paddingSize = (sizeof(NormalPage) + allocationGranularity - (sizeof(HeapObjectHeader) % allocationGranularity)) % allocationGranularity; |
- return sizeof(NormalPage) + paddingSize; |
- } |
- |
- |
- NormalPageHeap* heapForNormalPage(); |
- void clearObjectStartBitMap(); |
- |
-private: |
- HeapObjectHeader* findHeaderFromAddress(Address); |
- void populateObjectStartBitMap(); |
- bool isObjectStartBitMapComputed() { return m_objectStartBitMapComputed; } |
- |
- bool m_objectStartBitMapComputed; |
- uint8_t m_objectStartBitMap[reservedForObjectBitMap]; |
-}; |
- |
-// Large allocations are allocated as separate objects and linked in a list. |
-// |
-// In order to use the same memory allocation routines for everything allocated |
-// in the heap, large objects are considered heap pages containing only one |
-// object. |
-class LargeObjectPage final : public BasePage { |
-public: |
- LargeObjectPage(PageMemory*, BaseHeap*, size_t); |
- |
- Address payload() { return heapObjectHeader()->payload(); } |
- size_t payloadSize() { return m_payloadSize; } |
- Address payloadEnd() { return payload() + payloadSize(); } |
- bool containedInObjectPayload(Address address) |
- { |
- return payload() <= address && address < payloadEnd(); |
- } |
- |
- size_t objectPayloadSizeForTesting() override; |
- bool isEmpty() override; |
- void removeFromHeap() override; |
- void sweep() override; |
- void makeConsistentForGC() override; |
- void makeConsistentForMutator() override; |
-#if defined(ADDRESS_SANITIZER) |
- void poisonObjects(ThreadState::ObjectsToPoison, ThreadState::Poisoning) override; |
-#endif |
- void checkAndMarkPointer(Visitor*, Address) override; |
- void markOrphaned() override; |
- |
- void takeSnapshot(String dumpBaseName, size_t pageIndex, ThreadState::GCSnapshotInfo&, size_t* outFreeSize, size_t* outFreeCount) override; |
-#if ENABLE(GC_PROFILING) |
- const GCInfo* findGCInfo(Address) override; |
- void snapshot(TracedValue*, ThreadState::SnapshotInfo*) override; |
- void incrementMarkedObjectsAge() override; |
- void countMarkedObjects(ClassAgeCountsMap&) override; |
- void countObjectsToSweep(ClassAgeCountsMap&) override; |
-#endif |
-#if ENABLE(ASSERT) || ENABLE(GC_PROFILING) |
- // Returns true for any address that is on one of the pages that this |
- // large object uses. That ensures that we can use a negative result to |
- // populate the negative page cache. |
- bool contains(Address) override; |
-#endif |
- virtual size_t size() |
- { |
- return pageHeaderSize() + sizeof(HeapObjectHeader) + m_payloadSize; |
- } |
- static size_t pageHeaderSize() |
- { |
- // Compute the amount of padding we have to add to a header to make |
- // the size of the header plus the padding a multiple of 8 bytes. |
- size_t paddingSize = (sizeof(LargeObjectPage) + allocationGranularity - (sizeof(HeapObjectHeader) % allocationGranularity)) % allocationGranularity; |
- return sizeof(LargeObjectPage) + paddingSize; |
- } |
- bool isLargeObjectPage() override { return true; } |
- |
- HeapObjectHeader* heapObjectHeader() |
- { |
- Address headerAddress = address() + pageHeaderSize(); |
- return reinterpret_cast<HeapObjectHeader*>(headerAddress); |
- } |
- |
-#ifdef ANNOTATE_CONTIGUOUS_CONTAINER |
- void setIsVectorBackingPage() { m_isVectorBackingPage = true; } |
- bool isVectorBackingPage() const { return m_isVectorBackingPage; } |
-#endif |
- |
-private: |
- |
- size_t m_payloadSize; |
-#ifdef ANNOTATE_CONTIGUOUS_CONTAINER |
- bool m_isVectorBackingPage; |
-#endif |
-}; |
- |
-// A HeapDoesNotContainCache provides a fast way of taking an arbitrary |
-// pointer-sized word, and determining whether it cannot be interpreted as a |
-// pointer to an area that is managed by the garbage collected Blink heap. This |
-// is a cache of 'pages' that have previously been determined to be wholly |
-// outside of the heap. The size of these pages must be smaller than the |
-// allocation alignment of the heap pages. We determine off-heap-ness by |
-// rounding down the pointer to the nearest page and looking up the page in the |
-// cache. If there is a miss in the cache we can determine the status of the |
-// pointer precisely using the heap RegionTree. |
-// |
-// The HeapDoesNotContainCache is a negative cache, so it must be flushed when |
-// memory is added to the heap. |
-class HeapDoesNotContainCache { |
-public: |
- HeapDoesNotContainCache() |
- : m_entries(adoptArrayPtr(new Address[HeapDoesNotContainCache::numberOfEntries])) |
- , m_hasEntries(false) |
- { |
- // Start by flushing the cache in a non-empty state to initialize all the cache entries. |
- for (int i = 0; i < numberOfEntries; ++i) |
- m_entries[i] = nullptr; |
- } |
- |
- void flush(); |
- bool isEmpty() { return !m_hasEntries; } |
- |
- // Perform a lookup in the cache. |
- // |
- // If lookup returns false, the argument address was not found in |
- // the cache and it is unknown if the address is in the Blink |
- // heap. |
- // |
- // If lookup returns true, the argument address was found in the |
- // cache which means the address is not in the heap. |
- PLATFORM_EXPORT bool lookup(Address); |
- |
- // Add an entry to the cache. |
- PLATFORM_EXPORT void addEntry(Address); |
- |
-private: |
- static const int numberOfEntriesLog2 = 12; |
- static const int numberOfEntries = 1 << numberOfEntriesLog2; |
- |
- static size_t hash(Address); |
- |
- WTF::OwnPtr<Address[]> m_entries; |
- bool m_hasEntries; |
-}; |
- |
-class FreeList { |
-public: |
- FreeList(); |
- |
- void addToFreeList(Address, size_t); |
- void clear(); |
- |
- // Returns a bucket number for inserting a FreeListEntry of a given size. |
- // All FreeListEntries in the given bucket, n, have size >= 2^n. |
- static int bucketIndexForSize(size_t); |
- |
- // Returns true if the freelist snapshot is captured. |
- bool takeSnapshot(const String& dumpBaseName); |
- |
-#if ENABLE(GC_PROFILING) |
- struct PerBucketFreeListStats { |
- size_t entryCount; |
- size_t freeSize; |
- |
- PerBucketFreeListStats() : entryCount(0), freeSize(0) { } |
- }; |
- |
- void getFreeSizeStats(PerBucketFreeListStats bucketStats[], size_t& totalSize) const; |
-#endif |
- |
-#if ENABLE(ASSERT) || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) |
- static void zapFreedMemory(Address, size_t); |
-#endif |
- |
-private: |
- int m_biggestFreeListIndex; |
- |
- // All FreeListEntries in the nth list have size >= 2^n. |
- FreeListEntry* m_freeLists[blinkPageSizeLog2]; |
- |
- friend class NormalPageHeap; |
-}; |
- |
-// Each thread has a number of thread heaps (e.g., Generic heaps, |
-// typed heaps for Node, heaps for collection backings etc) |
-// and BaseHeap represents each thread heap. |
-// |
-// BaseHeap is a parent class of NormalPageHeap and LargeObjectHeap. |
-// NormalPageHeap represents a heap that contains NormalPages |
-// and LargeObjectHeap represents a heap that contains LargeObjectPages. |
-class PLATFORM_EXPORT BaseHeap { |
-public: |
- BaseHeap(ThreadState*, int); |
- virtual ~BaseHeap(); |
- void cleanupPages(); |
- |
- void takeSnapshot(const String& dumpBaseName, ThreadState::GCSnapshotInfo&); |
-#if ENABLE(ASSERT) || ENABLE(GC_PROFILING) |
- BasePage* findPageFromAddress(Address); |
-#endif |
- virtual void takeFreelistSnapshot(const String& dumpBaseName) { } |
-#if ENABLE(GC_PROFILING) |
- void snapshot(TracedValue*, ThreadState::SnapshotInfo*); |
- virtual void snapshotFreeList(TracedValue&) { } |
- |
- void countMarkedObjects(ClassAgeCountsMap&) const; |
- void countObjectsToSweep(ClassAgeCountsMap&) const; |
- void incrementMarkedObjectsAge(); |
-#endif |
- |
- virtual void clearFreeLists() { } |
- void makeConsistentForGC(); |
- void makeConsistentForMutator(); |
-#if ENABLE(ASSERT) |
- virtual bool isConsistentForGC() = 0; |
-#endif |
- size_t objectPayloadSizeForTesting(); |
- void prepareHeapForTermination(); |
- void prepareForSweep(); |
-#if defined(ADDRESS_SANITIZER) |
- void poisonHeap(ThreadState::ObjectsToPoison, ThreadState::Poisoning); |
-#endif |
- Address lazySweep(size_t, size_t gcInfoIndex); |
- void sweepUnsweptPage(); |
- // Returns true if we have swept all pages within the deadline. |
- // Returns false otherwise. |
- bool lazySweepWithDeadline(double deadlineSeconds); |
- void completeSweep(); |
- |
- ThreadState* threadState() { return m_threadState; } |
- int heapIndex() const { return m_index; } |
- |
-protected: |
- BasePage* m_firstPage; |
- BasePage* m_firstUnsweptPage; |
- |
-private: |
- virtual Address lazySweepPages(size_t, size_t gcInfoIndex) = 0; |
- |
- ThreadState* m_threadState; |
- |
- // Index into the page pools. This is used to ensure that the pages of the |
- // same type go into the correct page pool and thus avoid type confusion. |
- int m_index; |
-}; |
- |
-class PLATFORM_EXPORT NormalPageHeap final : public BaseHeap { |
-public: |
- NormalPageHeap(ThreadState*, int); |
- void addToFreeList(Address address, size_t size) |
- { |
- ASSERT(findPageFromAddress(address)); |
- ASSERT(findPageFromAddress(address + size - 1)); |
- m_freeList.addToFreeList(address, size); |
- } |
- void clearFreeLists() override; |
-#if ENABLE(ASSERT) |
- bool isConsistentForGC() override; |
- bool pagesToBeSweptContains(Address); |
-#endif |
- void takeFreelistSnapshot(const String& dumpBaseName) override; |
-#if ENABLE(GC_PROFILING) |
- void snapshotFreeList(TracedValue&) override; |
-#endif |
- |
- Address allocateObject(size_t allocationSize, size_t gcInfoIndex); |
- |
- void freePage(NormalPage*); |
- |
- bool coalesce(); |
- void promptlyFreeObject(HeapObjectHeader*); |
- bool expandObject(HeapObjectHeader*, size_t); |
- bool shrinkObject(HeapObjectHeader*, size_t); |
- void decreasePromptlyFreedSize(size_t size) { m_promptlyFreedSize -= size; } |
- |
-private: |
- void allocatePage(); |
- Address lazySweepPages(size_t, size_t gcInfoIndex) override; |
- Address outOfLineAllocate(size_t allocationSize, size_t gcInfoIndex); |
- Address currentAllocationPoint() const { return m_currentAllocationPoint; } |
- size_t remainingAllocationSize() const { return m_remainingAllocationSize; } |
- bool hasCurrentAllocationArea() const { return currentAllocationPoint() && remainingAllocationSize(); } |
- void setAllocationPoint(Address, size_t); |
- void updateRemainingAllocationSize(); |
- Address allocateFromFreeList(size_t, size_t gcInfoIndex); |
- |
- FreeList m_freeList; |
- Address m_currentAllocationPoint; |
- size_t m_remainingAllocationSize; |
- size_t m_lastRemainingAllocationSize; |
- |
- // The size of promptly freed objects in the heap. |
- size_t m_promptlyFreedSize; |
- |
-#if ENABLE(GC_PROFILING) |
- size_t m_cumulativeAllocationSize; |
- size_t m_allocationCount; |
- size_t m_inlineAllocationCount; |
-#endif |
-}; |
- |
-class LargeObjectHeap final : public BaseHeap { |
-public: |
- LargeObjectHeap(ThreadState*, int); |
- Address allocateLargeObjectPage(size_t, size_t gcInfoIndex); |
- void freeLargeObjectPage(LargeObjectPage*); |
-#if ENABLE(ASSERT) |
- bool isConsistentForGC() override { return true; } |
-#endif |
-private: |
- Address doAllocateLargeObjectPage(size_t, size_t gcInfoIndex); |
- Address lazySweepPages(size_t, size_t gcInfoIndex) override; |
-}; |
- |
-// Mask an address down to the enclosing oilpan heap base page. All oilpan heap |
-// pages are aligned at blinkPageBase plus the size of a guard size. |
-// FIXME: Remove PLATFORM_EXPORT once we get a proper public interface to our |
-// typed heaps. This is only exported to enable tests in HeapTest.cpp. |
-PLATFORM_EXPORT inline BasePage* pageFromObject(const void* object) |
-{ |
- Address address = reinterpret_cast<Address>(const_cast<void*>(object)); |
- BasePage* page = reinterpret_cast<BasePage*>(blinkPageAddress(address) + blinkGuardPageSize); |
- ASSERT(page->contains(address)); |
- return page; |
-} |
- |
template<typename T, bool = NeedsAdjustAndMark<T>::value> class ObjectAliveTrait; |
template<typename T> |
@@ -1235,129 +457,6 @@ public: \ |
#define EAGERLY_FINALIZE_WILL_BE_REMOVED() |
#endif |
-NO_SANITIZE_ADDRESS inline |
-size_t HeapObjectHeader::size() const |
-{ |
- size_t result = m_encoded & headerSizeMask; |
- // Large objects should not refer to header->size(). |
- // The actual size of a large object is stored in |
- // LargeObjectPage::m_payloadSize. |
- ASSERT(result != largeObjectSizeInHeader); |
- ASSERT(!pageFromObject(this)->isLargeObjectPage()); |
- return result; |
-} |
- |
-#if ENABLE(ASSERT) |
-NO_SANITIZE_ADDRESS inline |
-bool HeapObjectHeader::checkHeader() const |
-{ |
- return !pageFromObject(this)->orphaned() && m_magic == magic; |
-} |
-#endif |
- |
-inline Address HeapObjectHeader::payload() |
-{ |
- return reinterpret_cast<Address>(this) + sizeof(HeapObjectHeader); |
-} |
- |
-inline Address HeapObjectHeader::payloadEnd() |
-{ |
- return reinterpret_cast<Address>(this) + size(); |
-} |
- |
-NO_SANITIZE_ADDRESS inline |
-size_t HeapObjectHeader::payloadSize() |
-{ |
- size_t size = m_encoded & headerSizeMask; |
- if (UNLIKELY(size == largeObjectSizeInHeader)) { |
- ASSERT(pageFromObject(this)->isLargeObjectPage()); |
- return static_cast<LargeObjectPage*>(pageFromObject(this))->payloadSize(); |
- } |
- ASSERT(!pageFromObject(this)->isLargeObjectPage()); |
- return size - sizeof(HeapObjectHeader); |
-} |
- |
-inline HeapObjectHeader* HeapObjectHeader::fromPayload(const void* payload) |
-{ |
- Address addr = reinterpret_cast<Address>(const_cast<void*>(payload)); |
- HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(addr - sizeof(HeapObjectHeader)); |
- ASSERT(header->checkHeader()); |
- return header; |
-} |
- |
-NO_SANITIZE_ADDRESS inline |
-bool HeapObjectHeader::isMarked() const |
-{ |
- ASSERT(checkHeader()); |
- return m_encoded & headerMarkBitMask; |
-} |
- |
-NO_SANITIZE_ADDRESS inline |
-void HeapObjectHeader::mark() |
-{ |
- ASSERT(checkHeader()); |
- ASSERT(!isMarked()); |
- m_encoded = m_encoded | headerMarkBitMask; |
-} |
- |
-NO_SANITIZE_ADDRESS inline |
-void HeapObjectHeader::unmark() |
-{ |
- ASSERT(checkHeader()); |
- ASSERT(isMarked()); |
- m_encoded &= ~headerMarkBitMask; |
-} |
- |
-NO_SANITIZE_ADDRESS inline |
-bool HeapObjectHeader::isDead() const |
-{ |
- ASSERT(checkHeader()); |
- return m_encoded & headerDeadBitMask; |
-} |
- |
-NO_SANITIZE_ADDRESS inline |
-void HeapObjectHeader::markDead() |
-{ |
- ASSERT(checkHeader()); |
- ASSERT(!isMarked()); |
- m_encoded |= headerDeadBitMask; |
-} |
- |
-inline Address NormalPageHeap::allocateObject(size_t allocationSize, size_t gcInfoIndex) |
-{ |
-#if ENABLE(GC_PROFILING) |
- m_cumulativeAllocationSize += allocationSize; |
- ++m_allocationCount; |
-#endif |
- |
- if (LIKELY(allocationSize <= m_remainingAllocationSize)) { |
-#if ENABLE(GC_PROFILING) |
- ++m_inlineAllocationCount; |
-#endif |
- Address headerAddress = m_currentAllocationPoint; |
- m_currentAllocationPoint += allocationSize; |
- m_remainingAllocationSize -= allocationSize; |
- ASSERT(gcInfoIndex > 0); |
- new (NotNull, headerAddress) HeapObjectHeader(allocationSize, gcInfoIndex); |
- Address result = headerAddress + sizeof(HeapObjectHeader); |
- ASSERT(!(reinterpret_cast<uintptr_t>(result) & allocationMask)); |
- |
- SET_MEMORY_ACCESSIBLE(result, allocationSize - sizeof(HeapObjectHeader)); |
- ASSERT(findPageFromAddress(headerAddress + allocationSize - 1)); |
- return result; |
- } |
- return outOfLineAllocate(allocationSize, gcInfoIndex); |
-} |
- |
-template<typename Derived> |
-template<typename T> |
-void VisitorHelper<Derived>::handleWeakCell(Visitor* self, void* object) |
-{ |
- T** cell = reinterpret_cast<T**>(object); |
- if (*cell && !ObjectAliveTrait<T>::isHeapObjectAlive(*cell)) |
- *cell = nullptr; |
-} |
- |
inline Address Heap::allocateOnHeapIndex(ThreadState* state, size_t size, int heapIndex, size_t gcInfoIndex) |
{ |
ASSERT(state->isAllocationAllowed()); |
@@ -1406,6 +505,15 @@ Address Heap::reallocate(void* previous, size_t size) |
return address; |
} |
+template<typename Derived> |
+template<typename T> |
+void VisitorHelper<Derived>::handleWeakCell(Visitor* self, void* object) |
+{ |
+ T** cell = reinterpret_cast<T**>(object); |
+ if (*cell && !ObjectAliveTrait<T>::isHeapObjectAlive(*cell)) |
+ *cell = nullptr; |
+} |
+ |
} // namespace blink |
#endif // Heap_h |