diff options
Diffstat (limited to 'src/qml/memory/qv4mm.cpp')
-rw-r--r-- | src/qml/memory/qv4mm.cpp | 54 |
1 files changed, 43 insertions, 11 deletions
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 03e78df91a..b880c9c8d5 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -95,6 +95,8 @@ struct MemoryManager::Data uint maxShift; std::size_t maxChunkSize; QVector<PageAllocation> heapChunks; + std::size_t unmanagedHeapSize; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items. + std::size_t unmanagedHeapSizeGCLimit; struct LargeItem { LargeItem *next; @@ -121,6 +123,8 @@ struct MemoryManager::Data , totalAlloc(0) , maxShift(6) , maxChunkSize(32*1024) + , unmanagedHeapSize(0) + , unmanagedHeapSizeGCLimit(64 * 1024) , largeItems(0) , totalLargeItemsAllocated(0) { @@ -154,8 +158,10 @@ struct MemoryManager::Data namespace { -bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, ExecutionEngine *engine) +bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, ExecutionEngine *engine, std::size_t *unmanagedHeapSize) { + Q_ASSERT(unmanagedHeapSize); + bool isEmpty = true; Heap::Base *tail = &header->freeItems; // qDebug("chunkStart @ %p, size=%x, pos=%x", header->itemStart, header->itemSize, header->itemSize>>4); @@ -164,8 +170,8 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec #endif for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) { Heap::Base *m = reinterpret_cast<Heap::Base *>(item); -// qDebug("chunk @ %p, size = %lu, in use: %s, mark bit: %s", -// item, m->size, (m->inUse ? "yes" : "no"), (m->markBit ? "true" : "false")); +// qDebug("chunk @ %p, in use: %s, mark bit: %s", +// item, (m->inUse() ? "yes" : "no"), (m->isMarked() ? "true" : "false")); Q_ASSERT((qintptr) item % 16 == 0); @@ -180,6 +186,13 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec #ifdef V4_USE_VALGRIND VALGRIND_ENABLE_ERROR_REPORTING; #endif + if (std::size_t(header->itemSize) == MemoryManager::align(sizeof(Heap::String)) && m->vtable()->isString) { + std::size_t heapBytes = static_cast<Heap::String *>(m)->retainedTextSize(); + Q_ASSERT(*unmanagedHeapSize >= heapBytes); +// qDebug() << "-- it's a string holding on to" << heapBytes << "bytes"; + *unmanagedHeapSize -= heapBytes; + } + if (m->vtable()->destroy) m->vtable()->destroy(m); @@ -216,7 +229,7 @@ MemoryManager::MemoryManager(ExecutionEngine *engine) m_d->engine = engine; } -Heap::Base *MemoryManager::allocData(std::size_t size) +Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize) { if (m_d->aggressiveGC) runGC(); @@ -227,11 +240,27 @@ Heap::Base *MemoryManager::allocData(std::size_t size) Q_ASSERT(size >= 16); Q_ASSERT(size % 16 == 0); +// qDebug() << "unmanagedHeapSize:" << m_d->unmanagedHeapSize << "limit:" << m_d->unmanagedHeapSizeGCLimit << "unmanagedSize:" << unmanagedSize; + m_d->unmanagedHeapSize += unmanagedSize; + bool didGCRun = false; + if (m_d->unmanagedHeapSize > m_d->unmanagedHeapSizeGCLimit) { + runGC(); + + if (m_d->unmanagedHeapSizeGCLimit <= m_d->unmanagedHeapSize) + m_d->unmanagedHeapSizeGCLimit = std::max(m_d->unmanagedHeapSizeGCLimit, m_d->unmanagedHeapSize) * 2; + else if (m_d->unmanagedHeapSize * 4 <= m_d->unmanagedHeapSizeGCLimit) + m_d->unmanagedHeapSizeGCLimit /= 2; + else if (m_d->unmanagedHeapSizeGCLimit - m_d->unmanagedHeapSize < 5 * unmanagedSize) + // try preventing running the GC all the time when we're just below the threshold limit and manage to collect just enough to do this one allocation + m_d->unmanagedHeapSizeGCLimit += std::max(std::size_t(8 * 1024), 5 * unmanagedSize); + didGCRun = true; + } + size_t pos = size >> 4; // doesn't fit into a small bucket if (size >= MemoryManager::Data::MaxItemSize) { - if (m_d->totalLargeItemsAllocated > 8 * 1024 * 1024) + if (!didGCRun && m_d->totalLargeItemsAllocated > 8 * 1024 * 1024) runGC(); // we use malloc for this @@ -254,7 +283,7 @@ Heap::Base *MemoryManager::allocData(std::size_t size) } // try to free up space, otherwise allocate - if (m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && m_d->totalAlloc > (m_d->totalItems >> 1) && !m_d->aggressiveGC) { + if (!didGCRun && m_d->allocCount[pos] > (m_d->availableItems[pos] >> 1) && m_d->totalAlloc > (m_d->totalItems >> 1) && !m_d->aggressiveGC) { runGC(); header = m_d->nonFullChunks[pos]; if (header) { @@ -344,7 +373,7 @@ void MemoryManager::mark() for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { if (!(*it).isManaged()) continue; - if ((*it).managed()->d()->vtable() != QObjectWrapper::staticVTable()) + if (!(*it).as<QObjectWrapper>()) continue; QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper*>((*it).managed()); QObject *qobject = qobjectWrapper->object(); @@ -381,10 +410,8 @@ void MemoryManager::sweep(bool lastSweep) continue; // we need to call detroyObject on qobjectwrappers now, so that they can emit the destroyed // signal before we start sweeping the heap - if ((*it).managed()->d()->vtable() == QObjectWrapper::staticVTable()) { - QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper*>((*it).managed()); + if (QObjectWrapper *qobjectWrapper = (*it).as<QObjectWrapper>()) qobjectWrapper->destroyObject(lastSweep); - } (*it) = Primitive::undefinedValue(); } @@ -405,7 +432,7 @@ void MemoryManager::sweep(bool lastSweep) for (int i = 0; i < m_d->heapChunks.size(); ++i) { Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(m_d->heapChunks[i].base()); - chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], m_d->engine); + chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], m_d->engine, &m_d->unmanagedHeapSize); } QVector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin(); @@ -545,6 +572,11 @@ size_t MemoryManager::getLargeItemsMem() const return total; } +void MemoryManager::growUnmanagedHeapSizeUsage(size_t delta) +{ + m_d->unmanagedHeapSize += delta; +} + MemoryManager::~MemoryManager() { delete m_persistentValues; |