bmalloc: Don't use a whole page for metadata
authorggaren@apple.com <ggaren@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 21 Feb 2016 18:43:22 +0000 (18:43 +0000)
committerggaren@apple.com <ggaren@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 21 Feb 2016 18:43:22 +0000 (18:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=154510

Reviewed by Andreas Kling.

(1) Don't round up metadata to a page boundary. This saves 1.5% dirty
memory on iOS and 0.2% on Mac. It also enables a future patch to allocate
smaller chunks without wasting memory.

(2) Initialize metadata lazily. This saves dirty memory when the program
allocates primarily small or large objects (but not both), leaving some
metadata uninitialized.

* bmalloc.xcodeproj/project.pbxproj: Medium objects are gone now.

* bmalloc/BumpAllocator.h:
(bmalloc::BumpAllocator::refill): Added an ASSERT to help debug a bug
I cause while working on this patch.

* bmalloc/Heap.cpp:
(bmalloc::Heap::allocateSmallBumpRanges): Ditto.

(bmalloc::Heap::splitAndAllocate):
(bmalloc::Heap::allocateLarge): Updated for interface change.

* bmalloc/LargeChunk.h: Changed the boundaryTagCount calculation to
a static_assert.

Don't round up to page boundary. (See above.)

(bmalloc::LargeChunk::LargeChunk): Moved code here from LargeChunk::init.
A constructor is a more natural / automatic way to do this initialization.

* bmalloc/LargeObject.h:
(bmalloc::LargeObject::init): Deleted. Moved to LargeChunk.

* bmalloc/Sizes.h: Chagned largeChunkMetadataSize to a simpler constant
because metadata size no longer varies by page size.

* bmalloc/SmallChunk.h:
(bmalloc::SmallChunk::begin):
(bmalloc::SmallChunk::end):
(bmalloc::SmallChunk::lines):
(bmalloc::SmallChunk::pages): Use std::array to make begin/end
calculations easier.

(bmalloc::SmallChunk::SmallChunk): Treat our metadata like a series
of allocated objects. We used to avoid trampling our metadata by
starting object memory at the next page. Now we share the first page
between metadata and objects, and we account for metadata explicitly.

* bmalloc/SuperChunk.h:
(bmalloc::SuperChunk::SuperChunk):
(bmalloc::SuperChunk::smallChunk):
(bmalloc::SuperChunk::largeChunk):
(bmalloc::SuperChunk::create): Deleted. Don't eagerly run the SmallChunk
and LargeChunk constructors. We'll run them lazily as needed.

* bmalloc/VMHeap.cpp:
(bmalloc::VMHeap::VMHeap):
(bmalloc::VMHeap::allocateSmallChunk):
(bmalloc::VMHeap::allocateLargeChunk):
(bmalloc::VMHeap::allocateSuperChunk):
(bmalloc::VMHeap::grow): Deleted. Track small and large chunks explicitly
so we can initialize them lazily.

* bmalloc/VMHeap.h:
(bmalloc::VMHeap::allocateSmallPage):
(bmalloc::VMHeap::allocateLargeObject): Specify whether we're allocating
a small or large chunk since we don't allocate both at once anymore.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@196873 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/bmalloc/ChangeLog
Source/bmalloc/bmalloc.xcodeproj/project.pbxproj
Source/bmalloc/bmalloc/BumpAllocator.h
Source/bmalloc/bmalloc/Heap.cpp
Source/bmalloc/bmalloc/LargeChunk.h
Source/bmalloc/bmalloc/LargeObject.h
Source/bmalloc/bmalloc/Sizes.h
Source/bmalloc/bmalloc/SmallChunk.h
Source/bmalloc/bmalloc/SuperChunk.h
Source/bmalloc/bmalloc/VMHeap.cpp
Source/bmalloc/bmalloc/VMHeap.h

index eb6f0cb340f2027a69fea42df1a58f6ace842a82..571581a5060639a53bafdac0e07a952d0ce3be97 100644 (file)
@@ -1,3 +1,76 @@
+2016-02-21  Geoffrey Garen  <ggaren@apple.com>
+
+        bmalloc: Don't use a whole page for metadata
+        https://bugs.webkit.org/show_bug.cgi?id=154510
+
+        Reviewed by Andreas Kling.
+
+        (1) Don't round up metadata to a page boundary. This saves 1.5% dirty
+        memory on iOS and 0.2% on Mac. It also enables a future patch to allocate
+        smaller chunks without wasting memory.
+
+        (2) Initialize metadata lazily. This saves dirty memory when the program
+        allocates primarily small or large objects (but not both), leaving some
+        metadata uninitialized.
+
+        * bmalloc.xcodeproj/project.pbxproj: Medium objects are gone now.
+
+        * bmalloc/BumpAllocator.h:
+        (bmalloc::BumpAllocator::refill): Added an ASSERT to help debug a bug
+        I cause while working on this patch.
+
+        * bmalloc/Heap.cpp:
+        (bmalloc::Heap::allocateSmallBumpRanges): Ditto.
+
+        (bmalloc::Heap::splitAndAllocate):
+        (bmalloc::Heap::allocateLarge): Updated for interface change.
+
+        * bmalloc/LargeChunk.h: Changed the boundaryTagCount calculation to
+        a static_assert.
+
+        Don't round up to page boundary. (See above.)
+
+        (bmalloc::LargeChunk::LargeChunk): Moved code here from LargeChunk::init.
+        A constructor is a more natural / automatic way to do this initialization.
+
+        * bmalloc/LargeObject.h:
+        (bmalloc::LargeObject::init): Deleted. Moved to LargeChunk.
+
+        * bmalloc/Sizes.h: Chagned largeChunkMetadataSize to a simpler constant
+        because metadata size no longer varies by page size.
+
+        * bmalloc/SmallChunk.h:
+        (bmalloc::SmallChunk::begin):
+        (bmalloc::SmallChunk::end):
+        (bmalloc::SmallChunk::lines):
+        (bmalloc::SmallChunk::pages): Use std::array to make begin/end
+        calculations easier.
+
+        (bmalloc::SmallChunk::SmallChunk): Treat our metadata like a series
+        of allocated objects. We used to avoid trampling our metadata by
+        starting object memory at the next page. Now we share the first page
+        between metadata and objects, and we account for metadata explicitly.
+
+        * bmalloc/SuperChunk.h:
+        (bmalloc::SuperChunk::SuperChunk):
+        (bmalloc::SuperChunk::smallChunk):
+        (bmalloc::SuperChunk::largeChunk):
+        (bmalloc::SuperChunk::create): Deleted. Don't eagerly run the SmallChunk
+        and LargeChunk constructors. We'll run them lazily as needed.
+
+        * bmalloc/VMHeap.cpp:
+        (bmalloc::VMHeap::VMHeap):
+        (bmalloc::VMHeap::allocateSmallChunk):
+        (bmalloc::VMHeap::allocateLargeChunk):
+        (bmalloc::VMHeap::allocateSuperChunk):
+        (bmalloc::VMHeap::grow): Deleted. Track small and large chunks explicitly
+        so we can initialize them lazily.
+
+        * bmalloc/VMHeap.h:
+        (bmalloc::VMHeap::allocateSmallPage):
+        (bmalloc::VMHeap::allocateLargeObject): Specify whether we're allocating
+        a small or large chunk since we don't allocate both at once anymore.
+
 2016-02-20  Mark Lam  <mark.lam@apple.com>
 
         Use of inlined asm statements causes problems for -std=c99 builds.
index d38ada51da9a2ddf4dc80a36c0175d437440a648..fab5af8c3234e3f38795520985a1df7877f3c3de 100644 (file)
                                1448C2FD18F3752B00502839 /* api */,
                                14D9DB4D17F2865C00EAAB79 /* cache */,
                                147AAA9C18CE6010002201E4 /* heap: large */,
-                               147AAA9A18CE5FD3002201E4 /* heap: small | medium */,
+                               147AAA9A18CE5FD3002201E4 /* heap: small */,
                                14D9DB4E17F2866E00EAAB79 /* heap */,
                                14D9DB4F17F2868900EAAB79 /* stdlib */,
                                14B650C418F39F4800751968 /* Configurations */,
                        name = Products;
                        sourceTree = "<group>";
                };
-               147AAA9A18CE5FD3002201E4 /* heap: small | medium */ = {
+               147AAA9A18CE5FD3002201E4 /* heap: small */ = {
                        isa = PBXGroup;
                        children = (
                                147AAA8C18CD36A7002201E4 /* SmallChunk.h */,
                                1452478618BC757C00F80098 /* SmallLine.h */,
                                143E29ED18CAE90500FE8A0F /* SmallPage.h */,
                        );
-                       name = "heap: small | medium";
+                       name = "heap: small";
                        sourceTree = "<group>";
                };
                147AAA9C18CE6010002201E4 /* heap: large */ = {
index e61f4435c5e2144ce46fdc8cf88b6da2dbd8e3f0..a8a1ceabaa90ef2ea847a5c212ea37a6150f8342 100644 (file)
@@ -99,6 +99,7 @@ inline void BumpAllocator::refill(const BumpRange& bumpRange)
     BASSERT(!canAllocate());
     m_ptr = bumpRange.begin;
     m_remaining = bumpRange.objectCount;
+    BASSERT(canAllocate());
 }
 
 inline void BumpAllocator::clear()
index d468932e84611e65e2f4ef9930d86245b0c1bf20..7349d25b403f6842bc35abde7592d058bdc8e67f 100644 (file)
@@ -125,6 +125,7 @@ void Heap::allocateSmallBumpRanges(std::lock_guard<StaticMutex>& lock, size_t si
         // In a fragmented page, some free ranges might not fit in the cache.
         if (rangeCache.size() == rangeCache.capacity()) {
             m_smallPagesWithFreeLines[sizeClass].push(page);
+            BASSERT(allocator.canAllocate());
             return;
         }
 
@@ -153,6 +154,7 @@ void Heap::allocateSmallBumpRanges(std::lock_guard<StaticMutex>& lock, size_t si
             rangeCache.push({ begin, objectCount });
     }
 
+    BASSERT(allocator.canAllocate());
     page->setHasFreeLines(lock, false);
 }
 
@@ -304,7 +306,7 @@ inline LargeObject& Heap::splitAndAllocate(LargeObject& largeObject, size_t alig
     return largeObject;
 }
 
-void* Heap::allocateLarge(std::lock_guard<StaticMutex>&, size_t size)
+void* Heap::allocateLarge(std::lock_guard<StaticMutex>& lock, size_t size)
 {
     BASSERT(size <= largeMax);
     BASSERT(size >= largeMin);
@@ -312,7 +314,7 @@ void* Heap::allocateLarge(std::lock_guard<StaticMutex>&, size_t size)
 
     LargeObject largeObject = m_largeObjects.take(size);
     if (!largeObject)
-        largeObject = m_vmHeap.allocateLargeObject(size);
+        largeObject = m_vmHeap.allocateLargeObject(lock, size);
 
     if (largeObject.vmState().hasVirtual()) {
         m_isAllocatingPages = true;
@@ -326,7 +328,7 @@ void* Heap::allocateLarge(std::lock_guard<StaticMutex>&, size_t size)
     return largeObject.begin();
 }
 
-void* Heap::allocateLarge(std::lock_guard<StaticMutex>&, size_t alignment, size_t size, size_t unalignedSize)
+void* Heap::allocateLarge(std::lock_guard<StaticMutex>& lock, size_t alignment, size_t size, size_t unalignedSize)
 {
     BASSERT(size <= largeMax);
     BASSERT(size >= largeMin);
@@ -340,7 +342,7 @@ void* Heap::allocateLarge(std::lock_guard<StaticMutex>&, size_t alignment, size_
 
     LargeObject largeObject = m_largeObjects.take(alignment, size, unalignedSize);
     if (!largeObject)
-        largeObject = m_vmHeap.allocateLargeObject(alignment, size, unalignedSize);
+        largeObject = m_vmHeap.allocateLargeObject(lock, alignment, size, unalignedSize);
 
     if (largeObject.vmState().hasVirtual()) {
         m_isAllocatingPages = true;
index 22ed5bc69ceda564ba929135084e4600c44b72d1..fcbeac115aa9c71925a49998e7fe993b9f8b12ab 100644 (file)
 #include "ObjectType.h"
 #include "Sizes.h"
 #include "VMAllocate.h"
+#include <array>
 
 namespace bmalloc {
 
 class LargeChunk {
 public:
-    static LargeChunk* create();
+    LargeChunk();
     static LargeChunk* get(void*);
 
     static BeginTag* beginTag(void*);
@@ -46,8 +47,8 @@ public:
     char* end() { return reinterpret_cast<char*>(this) + largeChunkSize; }
 
 private:
-     // Round up to ensure 2 dummy boundary tags -- for the left and right sentinels.
-     static const size_t boundaryTagCount = max(2 * largeMin / sizeof(BoundaryTag), largeChunkSize / largeMin); 
+    static const size_t boundaryTagCount = largeChunkSize / largeMin;
+    static_assert(boundaryTagCount > 2, "LargeChunk must have space for two sentinel boundary tags");
 
     // Our metadata layout includes a left and right edge sentinel.
     // Metadata takes up enough space to leave at least the first two
@@ -63,23 +64,40 @@ private:
     //
     // We use the X's for boundary tags and the O's for edge sentinels.
 
-    BoundaryTag m_boundaryTags[boundaryTagCount];
-
-    // Align to vmPageSize to avoid sharing physical pages with metadata.
-    // Otherwise, we'll confuse the scavenger into trying to scavenge metadata.
-    // FIXME: Below #ifdef workaround fix should be removed after all linux based ports bump
-    // own gcc version. See https://bugs.webkit.org/show_bug.cgi?id=140162#c87
-#if BPLATFORM(IOS)
-    char m_memory[] __attribute__((aligned(16384)));
-    static_assert(vmPageSize == 16384, "vmPageSize and alignment must be same");
-#else
-    char m_memory[] __attribute__((aligned(4096)));
-    static_assert(vmPageSize == 4096, "vmPageSize and alignment must be same");
-#endif
+    std::array<BoundaryTag, boundaryTagCount> m_boundaryTags;
+    char m_memory[] __attribute__((aligned(largeAlignment)));
 };
 
-static_assert(largeChunkMetadataSize == sizeof(LargeChunk), "'largeChunkMetadataSize' should be the same number as sizeof(LargeChunk) or our computation in Sizes.h for 'largeMax' is wrong");
-static_assert(largeChunkMetadataSize + largeMax <= largeChunkSize, "We will think we can accommodate larger objects than we can in reality");
+static_assert(largeChunkMetadataSize == sizeof(LargeChunk), "Our largeChunkMetadataSize math in Sizes.h is wrong");
+static_assert(largeChunkMetadataSize + largeMax == largeChunkSize, "largeMax is too small or too big");
+
+inline LargeChunk::LargeChunk()
+{
+    Range range(begin(), end() - begin());
+    BASSERT(range.size() == largeMax);
+
+    BeginTag* beginTag = LargeChunk::beginTag(range.begin());
+    beginTag->setRange(range);
+    beginTag->setFree(true);
+    beginTag->setVMState(VMState::Virtual);
+
+    EndTag* endTag = LargeChunk::endTag(range.begin(), range.size());
+    endTag->init(beginTag);
+
+    // Mark the left and right edges of our range as allocated. This naturally
+    // prevents merging logic from overflowing left (into metadata) or right
+    // (beyond our chunk), without requiring special-case checks.
+
+    EndTag* leftSentinel = beginTag->prev();
+    BASSERT(leftSentinel >= m_boundaryTags.begin());
+    BASSERT(leftSentinel < m_boundaryTags.end());
+    leftSentinel->initSentinel();
+
+    BeginTag* rightSentinel = endTag->next();
+    BASSERT(rightSentinel >= m_boundaryTags.begin());
+    BASSERT(rightSentinel < m_boundaryTags.end());
+    rightSentinel->initSentinel();
+}
 
 inline LargeChunk* LargeChunk::get(void* object)
 {
index 58bf275b650b8675175ea0fe3160a224703bfa9b..08a4b141a84a323009b196884812c4a656f790c8 100644 (file)
@@ -35,8 +35,6 @@ namespace bmalloc {
 
 class LargeObject {
 public:
-    static Range init(LargeChunk*);
-
     LargeObject();
     LargeObject(void*);
 
@@ -271,33 +269,6 @@ inline void LargeObject::validate() const
     }
 }
 
-inline Range LargeObject::init(LargeChunk* chunk)
-{
-    Range range(chunk->begin(), chunk->end() - chunk->begin());
-
-    BeginTag* beginTag = LargeChunk::beginTag(range.begin());
-    beginTag->setRange(range);
-    beginTag->setFree(true);
-    beginTag->setVMState(VMState::Virtual);
-
-    EndTag* endTag = LargeChunk::endTag(range.begin(), range.size());
-    endTag->init(beginTag);
-
-    // Mark the left and right edges of our chunk as allocated. This naturally
-    // prevents merging logic from overflowing beyond our chunk, without requiring
-    // special-case checks.
-    
-    EndTag* leftSentinel = beginTag->prev();
-    BASSERT(leftSentinel >= static_cast<void*>(chunk));
-    leftSentinel->initSentinel();
-
-    BeginTag* rightSentinel = endTag->next();
-    BASSERT(rightSentinel < static_cast<void*>(range.begin()));
-    rightSentinel->initSentinel();
-    
-    return range;
-}
-
 } // namespace bmalloc
 
 #endif // LargeObject_h
index c9dfb469fb871b3e616f11b2c8f5d3e39489f045..6b7701def80f9c3410628fbbf4c55010994e8c75 100644 (file)
@@ -56,28 +56,23 @@ namespace Sizes {
     static const size_t superChunkSize = 2 * MB;
     static const size_t superChunkMask = ~(superChunkSize - 1);
 
-    static const size_t smallMax = 1024;
-    static const size_t smallLineSize = 256;
-    static const size_t smallLineCount = vmPageSize / smallLineSize;
-    static const size_t smallLineMask = ~(smallLineSize - 1ul);
-
     static const size_t smallChunkSize = superChunkSize / 2;
     static const size_t smallChunkOffset = superChunkSize / 2;
     static const size_t smallChunkMask = ~(smallChunkSize - 1ul);
 
+    static const size_t smallMax = 1024;
+    static const size_t smallLineSize = 256;
+    static const size_t smallLineCount = vmPageSize / smallLineSize;
+
     static const size_t largeChunkSize = superChunkSize / 2;
-#if BPLATFORM(IOS)
-    static const size_t largeChunkMetadataSize = 16 * kB;
-#else
-    static const size_t largeChunkMetadataSize = 4 * kB;
-#endif
     static const size_t largeChunkOffset = 0;
     static const size_t largeChunkMask = ~(largeChunkSize - 1ul);
 
     static const size_t largeAlignment = 64;
-    static const size_t largeMax = largeChunkSize - largeChunkMetadataSize;
     static const size_t largeMin = smallMax;
-    
+    static const size_t largeChunkMetadataSize = 4 * kB; // sizeof(LargeChunk)
+    static const size_t largeMax = largeChunkSize - largeChunkMetadataSize;
+
     static const size_t xLargeAlignment = vmPageSize;
     static const size_t xLargeMax = std::numeric_limits<size_t>::max() - xLargeAlignment; // Make sure that rounding up to xLargeAlignment does not overflow.
 
index 287b8e4b8e67703a3c40702001600175676307fa..c0e5bbb073bb1492b6a59222810973cf3edd2bbc 100644 (file)
@@ -35,37 +35,42 @@ namespace bmalloc {
 
 class SmallChunk {
 public:
+    SmallChunk(std::lock_guard<StaticMutex>&);
+
     static SmallChunk* get(void*);
 
     SmallPage* begin() { return SmallPage::get(SmallLine::get(m_memory)); }
-    SmallPage* end() { return &m_pages[pageCount]; }
+    SmallPage* end() { return m_pages.end(); }
+    
+    SmallLine* lines() { return m_lines.begin(); }
+    SmallPage* pages() { return m_pages.begin(); }
     
-    SmallLine* lines() { return m_lines; }
-    SmallPage* pages() { return m_pages; }
-
 private:
-    static_assert(!(vmPageSize % smallLineSize), "vmPageSize must be an even multiple of line size");
-    static_assert(!(smallChunkSize % smallLineSize), "chunk size must be an even multiple of line size");
-
-    static const size_t lineCount = smallChunkSize / smallLineSize;
-    static const size_t pageCount = smallChunkSize / vmPageSize;
-
-    SmallLine m_lines[lineCount];
-    SmallPage m_pages[pageCount];
-
-    // Align to vmPageSize to avoid sharing physical pages with metadata.
-    // Otherwise, we'll confuse the scavenger into trying to scavenge metadata.
-    // FIXME: Below #ifdef workaround fix should be removed after all linux based ports bump
-    // own gcc version. See https://bugs.webkit.org/show_bug.cgi?id=140162#c87
-#if BPLATFORM(IOS)
-    char m_memory[] __attribute__((aligned(16384)));
-    static_assert(vmPageSize == 16384, "vmPageSize and alignment must be same");
-#else
-    char m_memory[] __attribute__((aligned(4096)));
-    static_assert(vmPageSize == 4096, "vmPageSize and alignment must be same");
-#endif
+    std::array<SmallLine, smallChunkSize / smallLineSize> m_lines;
+    std::array<SmallPage, smallChunkSize / vmPageSize> m_pages;
+    char m_memory[] __attribute__((aligned(smallLineSize)));
 };
 
+static_assert(!(vmPageSize % smallLineSize), "vmPageSize must be an even multiple of line size");
+static_assert(!(smallChunkSize % smallLineSize), "chunk size must be an even multiple of line size");
+static_assert(
+    sizeof(SmallChunk) - vmPageSize % sizeof(SmallChunk) < vmPageSize - 2 * smallMax,
+        "the first page of object memory in a small chunk can't allocate smallMax");
+
+inline SmallChunk::SmallChunk(std::lock_guard<StaticMutex>& lock)
+{
+    // Track the memory used for metadata by allocating imaginary objects.
+    for (SmallLine* line = m_lines.begin(); line < SmallLine::get(m_memory); ++line) {
+        line->ref(lock, 1);
+
+        SmallPage* page = SmallPage::get(line);
+        page->ref(lock);
+    }
+
+    for (SmallPage* page = begin(); page != end(); ++page)
+        page->setHasFreeLines(lock, true);
+}
+
 inline SmallChunk* SmallChunk::get(void* object)
 {
     BASSERT(isSmall(object));
index 2222c0d512fcdf3b95689cd8c0a26293c6ee38cf..a19dc64d1690885aabefef210f89e1b9894ebe77 100644 (file)
@@ -33,37 +33,27 @@ namespace bmalloc {
 
 class SuperChunk {
 public:
-    static SuperChunk* create();
-
-    SmallChunk* smallChunk();
-    LargeChunk* largeChunk();
-
-private:
     SuperChunk();
-};
 
-inline SuperChunk* SuperChunk::create()
-{
-    void* result = static_cast<char*>(vmAllocate(superChunkSize, superChunkSize));
-    return new (result) SuperChunk;
-}
+    void* smallChunk();
+    void* largeChunk();
+};
 
 inline SuperChunk::SuperChunk()
 {
-    new (smallChunk()) SmallChunk;
-    new (largeChunk()) LargeChunk;
+    BASSERT(!test(this, ~superChunkMask));
+    BASSERT(!test(smallChunk(), ~smallChunkMask));
+    BASSERT(!test(largeChunk(), ~largeChunkMask));
 }
 
-inline SmallChunk* SuperChunk::smallChunk()
+inline void* SuperChunk::smallChunk()
 {
-    return reinterpret_cast<SmallChunk*>(
-        reinterpret_cast<char*>(this) + smallChunkOffset);
+    return reinterpret_cast<char*>(this) + smallChunkOffset;
 }
 
-inline LargeChunk* SuperChunk::largeChunk()
+inline void* SuperChunk::largeChunk()
 {
-    return reinterpret_cast<LargeChunk*>(
-        reinterpret_cast<char*>(this) + largeChunkOffset);
+    return reinterpret_cast<char*>(this) + largeChunkOffset;
 }
 
 } // namespace bmalloc
index b3dfea5205e5dade6df08333d7c66e3b0aa1bb3b..cd673c0b12f8c646d258be63d470171e77667464 100644 (file)
@@ -36,21 +36,37 @@ VMHeap::VMHeap()
 {
 }
 
-void VMHeap::grow()
+void VMHeap::allocateSmallChunk(std::lock_guard<StaticMutex>& lock)
 {
-    SuperChunk* superChunk = SuperChunk::create();
-#if BOS(DARWIN)
-    m_zone.addSuperChunk(superChunk);
-#endif
+    if (!m_smallChunks.size())
+        allocateSuperChunk(lock);
 
-    SmallChunk* smallChunk = superChunk->smallChunk();
-    for (auto* it = smallChunk->begin(); it != smallChunk->end(); ++it)
+    // We initialize chunks lazily to avoid dirtying their metadata pages.
+    SmallChunk* smallChunk = new (m_smallChunks.pop()->smallChunk()) SmallChunk(lock);
+    for (auto* it = smallChunk->begin(); it < smallChunk->end(); ++it)
         m_smallPages.push(it);
+}
+
+void VMHeap::allocateLargeChunk(std::lock_guard<StaticMutex>& lock)
+{
+    if (!m_largeChunks.size())
+        allocateSuperChunk(lock);
 
-    LargeChunk* largeChunk = superChunk->largeChunk();
-    LargeObject result(LargeObject::init(largeChunk).begin());
-    BASSERT(result.size() == largeMax);
-    m_largeObjects.insert(result);
+    // We initialize chunks lazily to avoid dirtying their metadata pages.
+    LargeChunk* largeChunk = new (m_largeChunks.pop()->largeChunk()) LargeChunk;
+    LargeObject largeObject(largeChunk->begin());
+    m_largeObjects.insert(largeObject);
+}
+
+void VMHeap::allocateSuperChunk(std::lock_guard<StaticMutex>&)
+{
+    SuperChunk* superChunk =
+        new (vmAllocate(superChunkSize, superChunkSize)) SuperChunk;
+    m_smallChunks.push(superChunk);
+    m_largeChunks.push(superChunk);
+#if BOS(DARWIN)
+    m_zone.addSuperChunk(superChunk);
+#endif
 }
 
 } // namespace bmalloc
index 7fb8cf806428a38de047e1ac9247410f81f5db7b..abc8c625e22d2e21275027964d467d40a633ffb0 100644 (file)
@@ -51,17 +51,23 @@ public:
     VMHeap();
 
     SmallPage* allocateSmallPage(std::lock_guard<StaticMutex>&);
-    LargeObject allocateLargeObject(size_t);
-    LargeObject allocateLargeObject(size_t, size_t, size_t);
+    LargeObject allocateLargeObject(std::lock_guard<StaticMutex>&, size_t);
+    LargeObject allocateLargeObject(std::lock_guard<StaticMutex>&, size_t, size_t, size_t);
 
     void deallocateSmallPage(std::unique_lock<StaticMutex>&, SmallPage*);
     void deallocateLargeObject(std::unique_lock<StaticMutex>&, LargeObject);
-
+    
 private:
-    void grow();
+    void allocateSmallChunk(std::lock_guard<StaticMutex>&);
+    void allocateLargeChunk(std::lock_guard<StaticMutex>&);
+    void allocateSuperChunk(std::lock_guard<StaticMutex>&);
 
     Vector<SmallPage*> m_smallPages;
     SegregatedFreeList m_largeObjects;
+
+    Vector<SuperChunk*> m_smallChunks;
+    Vector<SuperChunk*> m_largeChunks;
+
 #if BOS(DARWIN)
     Zone m_zone;
 #endif
@@ -70,19 +76,18 @@ private:
 inline SmallPage* VMHeap::allocateSmallPage(std::lock_guard<StaticMutex>& lock)
 {
     if (!m_smallPages.size())
-        grow();
+        allocateSmallChunk(lock);
 
     SmallPage* page = m_smallPages.pop();
-    page->setHasFreeLines(lock, true);
     vmAllocatePhysicalPages(page->begin()->begin(), vmPageSize);
     return page;
 }
 
-inline LargeObject VMHeap::allocateLargeObject(size_t size)
+inline LargeObject VMHeap::allocateLargeObject(std::lock_guard<StaticMutex>& lock, size_t size)
 {
     LargeObject largeObject = m_largeObjects.take(size);
     if (!largeObject) {
-        grow();
+        allocateLargeChunk(lock);
         largeObject = m_largeObjects.take(size);
         BASSERT(largeObject);
     }
@@ -90,11 +95,11 @@ inline LargeObject VMHeap::allocateLargeObject(size_t size)
     return largeObject;
 }
 
-inline LargeObject VMHeap::allocateLargeObject(size_t alignment, size_t size, size_t unalignedSize)
+inline LargeObject VMHeap::allocateLargeObject(std::lock_guard<StaticMutex>& lock, size_t alignment, size_t size, size_t unalignedSize)
 {
     LargeObject largeObject = m_largeObjects.take(alignment, size, unalignedSize);
     if (!largeObject) {
-        grow();
+        allocateLargeChunk(lock);
         largeObject = m_largeObjects.take(alignment, size, unalignedSize);
         BASSERT(largeObject);
     }