Changeset 92084 in webkit


Ignore:
Timestamp:
Jul 31, 2011 12:03:51 PM (13 years ago)
Author:
fpizlo@apple.com
Message:

The JSC garbage collector returns memory to the operating system too
eagerly.
https://bugs.webkit.org/show_bug.cgi?id=65382

Reviewed by Oliver Hunt.

This introduces a memory reuse model similar to the one in FastMalloc.
A periodic scavenger thread runs in the background and returns half the
free memory to the OS on each timer fire. New block allocations first
attempt to get the memory from the collector's internal pool, reverting
to OS allocation only when this pool is empty.

  • heap/Heap.cpp:

(JSC::Heap::Heap):
(JSC::Heap::~Heap):
(JSC::Heap::destroy):
(JSC::Heap::waitForRelativeTimeWhileHoldingLock):
(JSC::Heap::waitForRelativeTime):
(JSC::Heap::blockFreeingThreadStartFunc):
(JSC::Heap::blockFreeingThreadMain):
(JSC::Heap::allocateBlock):
(JSC::Heap::freeBlocks):
(JSC::Heap::releaseFreeBlocks):

  • heap/Heap.h:
  • heap/MarkedBlock.cpp:

(JSC::MarkedBlock::destroy):
(JSC::MarkedBlock::MarkedBlock):
(JSC::MarkedBlock::initForCellSize):
(JSC::MarkedBlock::reset):

  • heap/MarkedBlock.h:
  • wtf/Platform.h:
Location:
trunk/Source/JavaScriptCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r92071 r92084  
     12011-07-31  Filip Pizlo  <fpizlo@apple.com>
     2
     3        The JSC garbage collector returns memory to the operating system too
     4        eagerly.
     5        https://bugs.webkit.org/show_bug.cgi?id=65382
     6
     7        Reviewed by Oliver Hunt.
     8       
     9        This introduces a memory reuse model similar to the one in FastMalloc.
     10        A periodic scavenger thread runs in the background and returns half the
     11        free memory to the OS on each timer fire.  New block allocations first
     12        attempt to get the memory from the collector's internal pool, reverting
     13        to OS allocation only when this pool is empty.
     14
     15        * heap/Heap.cpp:
     16        (JSC::Heap::Heap):
     17        (JSC::Heap::~Heap):
     18        (JSC::Heap::destroy):
     19        (JSC::Heap::waitForRelativeTimeWhileHoldingLock):
     20        (JSC::Heap::waitForRelativeTime):
     21        (JSC::Heap::blockFreeingThreadStartFunc):
     22        (JSC::Heap::blockFreeingThreadMain):
     23        (JSC::Heap::allocateBlock):
     24        (JSC::Heap::freeBlocks):
     25        (JSC::Heap::releaseFreeBlocks):
     26        * heap/Heap.h:
     27        * heap/MarkedBlock.cpp:
     28        (JSC::MarkedBlock::destroy):
     29        (JSC::MarkedBlock::MarkedBlock):
     30        (JSC::MarkedBlock::initForCellSize):
     31        (JSC::MarkedBlock::reset):
     32        * heap/MarkedBlock.h:
     33        * wtf/Platform.h:
     34
    1352011-07-30  Filip Pizlo  <fpizlo@apple.com>
    236
  • trunk/Source/JavaScriptCore/heap/Heap.cpp

    r92046 r92084  
    245245    m_newSpace.setHighWaterMark(minBytesPerCycle);
    246246    (*m_activityCallback)();
     247#if ENABLE(LAZY_BLOCK_FREEING)
     248    m_numberOfFreeBlocks = 0;
     249    m_blockFreeingThread = createThread(blockFreeingThreadStartFunc, this, "JavaScriptCore::BlockFree");
     250    ASSERT(m_blockFreeingThread);
     251#endif
    247252}
    248253
    249254Heap::~Heap()
    250255{
     256#if ENABLE(LAZY_BLOCK_FREEING)
     257    // destroy our thread
     258    {
     259        MutexLocker locker(m_freeBlockLock);
     260        m_blockFreeingThreadShouldQuit = true;
     261        m_freeBlockCondition.broadcast();
     262    }
     263    waitForThreadCompletion(m_blockFreeingThread, 0);
     264#endif
     265   
    251266    // The destroy function must already have been called, so assert this.
    252267    ASSERT(!m_globalData);
     
    280295    shrink();
    281296    ASSERT(!size());
     297   
     298#if ENABLE(LAZY_BLOCK_FREEING)
     299    releaseFreeBlocks();
     300#endif
    282301
    283302    m_globalData = 0;
    284303}
     304
     305#if ENABLE(LAZY_BLOCK_FREEING)
     306void Heap::waitForRelativeTimeWhileHoldingLock(double relative)
     307{
     308    if (m_blockFreeingThreadShouldQuit)
     309        return;
     310    m_freeBlockCondition.timedWait(m_freeBlockLock, currentTime() + relative);
     311}
     312
     313void Heap::waitForRelativeTime(double relative)
     314{
     315    // If this returns early, that's fine, so long as it doesn't do it too
     316    // frequently. It would only be a bug if this function failed to return
     317    // when it was asked to do so.
     318   
     319    MutexLocker locker(m_freeBlockLock);
     320    waitForRelativeTimeWhileHoldingLock(relative);
     321}
     322
     323void* Heap::blockFreeingThreadStartFunc(void* heap)
     324{
     325    static_cast<Heap*>(heap)->blockFreeingThreadMain();
     326    return 0;
     327}
     328
     329void Heap::blockFreeingThreadMain()
     330{
     331    while (!m_blockFreeingThreadShouldQuit) {
     332        // Generally wait for one second before scavenging free blocks. This
     333        // may return early, particularly when we're being asked to quit.
     334        waitForRelativeTime(1.0);
     335        if (m_blockFreeingThreadShouldQuit)
     336            break;
     337       
     338        // Now process the list of free blocks. Keep freeing until half of the
     339        // blocks that are currently on the list are gone. Assume that a size_t
     340        // field can be accessed atomically.
     341        size_t currentNumberOfFreeBlocks = m_numberOfFreeBlocks;
     342        if (!currentNumberOfFreeBlocks)
     343            continue;
     344       
     345        size_t desiredNumberOfFreeBlocks = currentNumberOfFreeBlocks / 2;
     346       
     347        while (!m_blockFreeingThreadShouldQuit) {
     348            MarkedBlock* block;
     349            {
     350                MutexLocker locker(m_freeBlockLock);
     351                if (m_numberOfFreeBlocks <= desiredNumberOfFreeBlocks)
     352                    block = 0;
     353                else {
     354                    block = m_freeBlocks.removeHead();
     355                    ASSERT(block);
     356                    m_numberOfFreeBlocks--;
     357                }
     358            }
     359           
     360            if (!block)
     361                break;
     362           
     363            MarkedBlock::destroy(block);
     364        }
     365    }
     366}
     367#endif // ENABLE(LAZY_BLOCK_FREEING)
    285368
    286369void Heap::reportExtraMemoryCostSlowCase(size_t cost)
     
    614697MarkedBlock* Heap::allocateBlock(size_t cellSize)
    615698{
    616     MarkedBlock* block = MarkedBlock::create(this, cellSize);
     699    MarkedBlock* block;
     700   
     701#if !ENABLE(LAZY_BLOCK_FREEING)
     702    block = MarkedBlock::create(this, cellSize);
     703#else
     704    {
     705        MutexLocker locker(m_freeBlockLock);
     706        if (m_numberOfFreeBlocks) {
     707            block = m_freeBlocks.removeHead();
     708            ASSERT(block);
     709            m_numberOfFreeBlocks--;
     710        } else
     711            block = 0;
     712    }
     713    if (block)
     714        block->initForCellSize(cellSize);
     715    else
     716        block = MarkedBlock::create(this, cellSize);
     717#endif
     718   
    617719    m_blocks.add(block);
    618720
     
    627729
    628730        m_blocks.remove(block);
     731        block->reset();
     732#if !ENABLE(LAZY_BLOCK_FREEING)
    629733        MarkedBlock::destroy(block);
     734#else
     735        MutexLocker locker(m_freeBlockLock);
     736        m_freeBlocks.append(block);
     737        m_numberOfFreeBlocks++;
     738#endif
    630739    }
    631740}
     
    638747}
    639748
     749#if ENABLE(LAZY_BLOCK_FREEING)
     750void Heap::releaseFreeBlocks()
     751{
     752    while (true) {
     753        MarkedBlock* block;
     754        {
     755            MutexLocker locker(m_freeBlockLock);
     756            if (!m_numberOfFreeBlocks)
     757                block = 0;
     758            else {
     759                block = m_freeBlocks.removeHead();
     760                ASSERT(block);
     761                m_numberOfFreeBlocks--;
     762            }
     763        }
     764       
     765        if (!block)
     766            break;
     767       
     768        MarkedBlock::destroy(block);
     769    }
     770}
     771#endif
     772
    640773#if ENABLE(GGC)
    641774void Heap::writeBarrierSlowCase(const JSCell* owner, JSCell* cell)
  • trunk/Source/JavaScriptCore/heap/Heap.h

    r91199 r92084  
    5353
    5454    enum OperationInProgress { NoOperation, Allocation, Collection };
    55 
     55   
    5656    class Heap {
    5757        WTF_MAKE_NONCOPYABLE(Heap);
     
    145145        void collect(SweepToggle);
    146146        void shrink();
     147        void releaseFreeBlocks();
    147148        void sweep();
    148149
     
    150151
    151152        static void writeBarrierSlowCase(const JSCell*, JSCell*);
     153       
     154#if ENABLE(LAZY_BLOCK_FREEING)
     155        void waitForRelativeTimeWhileHoldingLock(double relative);
     156        void waitForRelativeTime(double relative);
     157        void blockFreeingThreadMain();
     158        static void* blockFreeingThreadStartFunc(void* heap);
     159#endif
    152160
    153161        OperationInProgress m_operationInProgress;
    154162        NewSpace m_newSpace;
    155163        MarkedBlockSet m_blocks;
     164
     165#if ENABLE(LAZY_BLOCK_FREEING)
     166        DoublyLinkedList<MarkedBlock> m_freeBlocks;
     167        size_t m_numberOfFreeBlocks;
     168       
     169        ThreadIdentifier m_blockFreeingThread;
     170        Mutex m_freeBlockLock;
     171        ThreadCondition m_freeBlockCondition;
     172        bool m_blockFreeingThreadShouldQuit;
     173#endif
    156174
    157175        size_t m_extraCost;
  • trunk/Source/JavaScriptCore/heap/MarkedBlock.cpp

    r92046 r92084  
    4343void MarkedBlock::destroy(MarkedBlock* block)
    4444{
    45     for (size_t i = block->firstAtom(); i < block->m_endAtom; i += block->m_atomsPerCell)
    46         reinterpret_cast<JSCell*>(&block->atoms()[i])->~JSCell();
    4745    block->m_allocation.deallocate();
    4846}
     
    5351    , m_heap(heap)
    5452{
     53    initForCellSize(cellSize);
     54}
     55
     56void MarkedBlock::initForCellSize(size_t cellSize)
     57{
    5558    m_atomsPerCell = (cellSize + atomSize - 1) / atomSize;
    5659    m_endAtom = atomsPerBlock - m_atomsPerCell + 1;
     60}
     61
     62void MarkedBlock::reset()
     63{
     64    for (size_t i = firstAtom(); i < m_endAtom; i += m_atomsPerCell)
     65        reinterpret_cast<JSCell*>(&atoms()[i])->~JSCell();
    5766}
    5867
  • trunk/Source/JavaScriptCore/heap/MarkedBlock.h

    r92046 r92084  
    9595        FreeCell* lazySweep();
    9696       
     97        void initForCellSize(size_t cellSize);
     98       
    9799        // These should be called immediately after a block is created.
    98100        // Blessing for fast path creates a linked list, while blessing for
     
    100102        FreeCell* blessNewBlockForFastPath();
    101103        void blessNewBlockForSlowPath();
     104       
     105        void reset();
    102106       
    103107        // This unmarks all cells on the free list, and allocates dummy JSCells
  • trunk/Source/JavaScriptCore/wtf/Platform.h

    r92046 r92084  
    11061106#endif
    11071107
     1108#if ENABLE(SINGLE_THREADED)
     1109#undef ENABLE_LAZY_BLOCK_FREEING
     1110#define ENABLE_LAZY_BLOCK_FREEING 0
     1111#else
     1112#define ENABLE_LAZY_BLOCK_FREEING 1
     1113#endif
     1114
    11081115#if !defined(ENABLE_PAN_SCROLLING) && OS(WINDOWS)
    11091116#define ENABLE_PAN_SCROLLING 1
Note: See TracChangeset for help on using the changeset viewer.