diff options
Diffstat (limited to 'src/qml/memory/qv4mm.cpp')
-rw-r--r-- | src/qml/memory/qv4mm.cpp | 138 |
1 files changed, 83 insertions, 55 deletions
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 9924d37fdc..6ef2380561 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -61,6 +61,10 @@ #include <valgrind/memcheck.h> #endif +#ifdef V4_USE_HEAPTRACK +#include <heaptrack_api.h> +#endif + #if OS(QNX) #include <sys/storage.h> // __tls() #endif @@ -69,7 +73,7 @@ #include <pthread_np.h> #endif -#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT (std::size_t)128*1024 +#define MIN_UNMANAGED_HEAPSIZE_GC_LIMIT std::size_t(128 * 1024) using namespace WTF; @@ -80,9 +84,9 @@ static uint maxShiftValue() static uint result = 0; if (!result) { result = 6; - if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAXBLOCK_SHIFT"))) { + if (Q_UNLIKELY(qEnvironmentVariableIsSet(QV4_MM_MAXBLOCK_SHIFT))) { bool ok; - const uint overrideValue = qgetenv("QV4_MM_MAXBLOCK_SHIFT").toUInt(&ok); + const uint overrideValue = qgetenv(QV4_MM_MAXBLOCK_SHIFT).toUInt(&ok); if (ok && overrideValue <= 11 && overrideValue > 0) result = overrideValue; } @@ -95,9 +99,9 @@ static std::size_t maxChunkSizeValue() static std::size_t result = 0; if (!result) { result = 32 * 1024; - if (Q_UNLIKELY(qEnvironmentVariableIsSet("QV4_MM_MAX_CHUNK_SIZE"))) { + if (Q_UNLIKELY(qEnvironmentVariableIsSet(QV4_MM_MAX_CHUNK_SIZE))) { bool ok; - const std::size_t overrideValue = qgetenv("QV4_MM_MAX_CHUNK_SIZE").toUInt(&ok); + const std::size_t overrideValue = qgetenv(QV4_MM_MAX_CHUNK_SIZE).toUInt(&ok); if (ok) result = overrideValue; } @@ -109,29 +113,20 @@ using namespace QV4; struct MemoryManager::Data { + const size_t pageSize; + struct ChunkHeader { Heap::Base freeItems; ChunkHeader *nextNonFull; char *itemStart; char *itemEnd; - int itemSize; + unsigned itemSize; }; - bool gcBlocked; - bool aggressiveGC; - bool gcStats; ExecutionEngine *engine; - enum { MaxItemSize = 512 }; - ChunkHeader *nonFullChunks[MaxItemSize/16]; - uint nChunks[MaxItemSize/16]; - uint availableItems[MaxItemSize/16]; - uint allocCount[MaxItemSize/16]; - int totalItems; - int totalAlloc; - uint maxShift; std::size_t maxChunkSize; - QVector<PageAllocation> heapChunks; + std::vector<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; @@ -148,24 +143,39 @@ struct MemoryManager::Data LargeItem *largeItems; std::size_t totalLargeItemsAllocated; + enum { MaxItemSize = 512 }; + ChunkHeader *nonFullChunks[MaxItemSize/16]; + uint nChunks[MaxItemSize/16]; + uint availableItems[MaxItemSize/16]; + uint allocCount[MaxItemSize/16]; + int totalItems; + int totalAlloc; + uint maxShift; + + bool gcBlocked; + bool aggressiveGC; + bool gcStats; + bool unused; // suppress padding warning + // statistics: #ifdef DETAILED_MM_STATS QVector<unsigned> allocSizeCounters; #endif // DETAILED_MM_STATS Data() - : gcBlocked(false) - , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) - , gcStats(!qEnvironmentVariableIsEmpty("QV4_MM_STATS")) + : pageSize(WTF::pageSize()) , engine(0) - , totalItems(0) - , totalAlloc(0) - , maxShift(maxShiftValue()) , maxChunkSize(maxChunkSizeValue()) , unmanagedHeapSize(0) , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT) , largeItems(0) , totalLargeItemsAllocated(0) + , totalItems(0) + , totalAlloc(0) + , maxShift(maxShiftValue()) + , gcBlocked(false) + , aggressiveGC(!qEnvironmentVariableIsEmpty("QV4_MM_AGGRESSIVE_GC")) + , gcStats(!qEnvironmentVariableIsEmpty(QV4_MM_STATS)) { memset(nonFullChunks, 0, sizeof(nonFullChunks)); memset(nChunks, 0, sizeof(nChunks)); @@ -175,8 +185,8 @@ struct MemoryManager::Data ~Data() { - for (QVector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) { - Q_V4_PROFILE_DEALLOC(engine, 0, i->size(), Profiling::HeapPage); + for (std::vector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) { + Q_V4_PROFILE_DEALLOC(engine, i->size(), Profiling::HeapPage); i->deallocate(); } } @@ -199,7 +209,7 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec // qDebug("chunk @ %p, in use: %s, mark bit: %s", // item, (m->inUse() ? "yes" : "no"), (m->isMarked() ? "true" : "false")); - Q_ASSERT((qintptr) item % 16 == 0); + Q_ASSERT(qintptr(item) % 16 == 0); if (m->isMarked()) { Q_ASSERT(m->inUse()); @@ -219,15 +229,20 @@ bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, Exec *unmanagedHeapSize -= heapBytes; } - if (m->vtable()->destroy) + if (m->vtable()->destroy) { m->vtable()->destroy(m); + m->_checkIsDestroyed(); + } - memset(m, 0, header->itemSize); + memset(m, 0, sizeof(Heap::Base)); #ifdef V4_USE_VALGRIND VALGRIND_DISABLE_ERROR_REPORTING; VALGRIND_MEMPOOL_FREE(engine->memoryManager, m); #endif - Q_V4_PROFILE_DEALLOC(engine, m, header->itemSize, Profiling::SmallItem); +#ifdef V4_USE_HEAPTRACK + heaptrack_report_free(m); +#endif + Q_V4_PROFILE_DEALLOC(engine, header->itemSize, Profiling::SmallItem); ++(*itemsInUse); } // Relink all free blocks to rewrite references to any released chunk. @@ -290,10 +305,11 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize runGC(); // we use malloc for this - MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>( - malloc(Q_V4_PROFILE_ALLOC(engine, size + sizeof(MemoryManager::Data::LargeItem), - Profiling::LargeItem))); - memset(item, 0, size + sizeof(MemoryManager::Data::LargeItem)); + const size_t totalSize = size + sizeof(MemoryManager::Data::LargeItem); + Q_V4_PROFILE_ALLOC(engine, totalSize, Profiling::LargeItem); + MemoryManager::Data::LargeItem *item = + static_cast<MemoryManager::Data::LargeItem *>(malloc(totalSize)); + memset(item, 0, totalSize); item->next = m_d->largeItems; item->size = size; m_d->largeItems = item; @@ -325,14 +341,14 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize if (shift > m_d->maxShift) shift = m_d->maxShift; std::size_t allocSize = m_d->maxChunkSize*(size_t(1) << shift); - allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); - PageAllocation allocation = PageAllocation::allocate( - Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage), - OSAllocator::JSGCHeapPages); - m_d->heapChunks.append(allocation); + allocSize = roundUpToMultipleOf(m_d->pageSize, allocSize); + Q_V4_PROFILE_ALLOC(engine, allocSize, Profiling::HeapPage); + PageAllocation allocation = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); + m_d->heapChunks.push_back(allocation); header = reinterpret_cast<Data::ChunkHeader *>(allocation.base()); - header->itemSize = int(size); + Q_ASSERT(size <= UINT_MAX); + header->itemSize = unsigned(size); header->itemStart = reinterpret_cast<char *>(allocation.base()) + roundUpToMultipleOf(16, sizeof(Data::ChunkHeader)); header->itemEnd = reinterpret_cast<char *>(allocation.base()) + allocation.size() - header->itemSize; @@ -348,19 +364,26 @@ Heap::Base *MemoryManager::allocData(std::size_t size, std::size_t unmanagedSize } last->setNextFree(0); m = header->freeItems.nextFree(); - const size_t increase = (header->itemEnd - header->itemStart) / header->itemSize; + Q_ASSERT(header->itemEnd >= header->itemStart); + const size_t increase = quintptr(header->itemEnd - header->itemStart) / header->itemSize; m_d->availableItems[pos] += uint(increase); m_d->totalItems += int(increase); #ifdef V4_USE_VALGRIND VALGRIND_MAKE_MEM_NOACCESS(allocation.base(), allocSize); VALGRIND_MEMPOOL_ALLOC(this, header, sizeof(Data::ChunkHeader)); #endif +#ifdef V4_USE_HEAPTRACK + heaptrack_report_alloc(header, sizeof(Data::ChunkHeader)); +#endif } found: #ifdef V4_USE_VALGRIND VALGRIND_MEMPOOL_ALLOC(this, m, size); #endif +#ifdef V4_USE_HEAPTRACK + heaptrack_report_alloc(m, size); +#endif Q_V4_PROFILE_ALLOC(engine, size, Profiling::SmallItem); ++m_d->allocCount[pos]; @@ -375,6 +398,7 @@ static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) { while (engine->jsStackTop > markBase) { Heap::Base *h = engine->popForGC(); + Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen. Q_ASSERT (h->vtable()->markObjects); h->vtable()->markObjects(h, engine); } @@ -431,7 +455,7 @@ void MemoryManager::sweep(bool lastSweep) for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { if (!(*it).isManaged()) continue; - Managed *m = (*it).as<Managed>(); + Managed *m = (*it).managed(); if (m->markBit()) continue; // we need to call destroyObject on qobjectwrappers now, so that they can emit the destroyed @@ -477,29 +501,33 @@ void MemoryManager::sweep(bool lastSweep) } } - bool *chunkIsEmpty = (bool *)alloca(m_d->heapChunks.size() * sizeof(bool)); + bool *chunkIsEmpty = static_cast<bool *>(alloca(m_d->heapChunks.size() * sizeof(bool))); uint itemsInUse[MemoryManager::Data::MaxItemSize/16]; memset(itemsInUse, 0, sizeof(itemsInUse)); memset(m_d->nonFullChunks, 0, sizeof(m_d->nonFullChunks)); - for (int i = 0; i < m_d->heapChunks.size(); ++i) { + for (size_t 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], engine, &m_d->unmanagedHeapSize); } - QVector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin(); - for (int i = 0; i < m_d->heapChunks.size(); ++i) { + std::vector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin(); + for (size_t i = 0; i < m_d->heapChunks.size(); ++i) { Q_ASSERT(chunkIter != m_d->heapChunks.end()); Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(chunkIter->base()); const size_t pos = header->itemSize >> 4; - const size_t decrease = (header->itemEnd - header->itemStart) / header->itemSize; + Q_ASSERT(header->itemEnd >= header->itemStart); + const size_t decrease = quintptr(header->itemEnd - header->itemStart) / header->itemSize; // Release that chunk if it could have been spared since the last GC run without any difference. if (chunkIsEmpty[i] && m_d->availableItems[pos] - decrease >= itemsInUse[pos]) { - Q_V4_PROFILE_DEALLOC(engine, 0, chunkIter->size(), Profiling::HeapPage); + Q_V4_PROFILE_DEALLOC(engine, chunkIter->size(), Profiling::HeapPage); #ifdef V4_USE_VALGRIND VALGRIND_MEMPOOL_FREE(this, header); #endif +#ifdef V4_USE_HEAPTRACK + heaptrack_report_free(header); +#endif --m_d->nChunks[pos]; m_d->availableItems[pos] -= uint(decrease); m_d->totalItems -= int(decrease); @@ -528,8 +556,8 @@ void MemoryManager::sweep(bool lastSweep) m->vtable()->destroy(m); *last = i->next; - free(Q_V4_PROFILE_DEALLOC(engine, i, i->size + sizeof(Data::LargeItem), - Profiling::LargeItem)); + Q_V4_PROFILE_DEALLOC(engine, i->size + sizeof(Data::LargeItem), Profiling::LargeItem); + free(i); i = *last; } @@ -574,7 +602,7 @@ void MemoryManager::runGC() qint64 markTime = t.restart(); const size_t usedBefore = getUsedMem(); const size_t largeItemsBefore = getLargeItemsMem(); - int chunksBefore = m_d->heapChunks.size(); + size_t chunksBefore = m_d->heapChunks.size(); sweep(); const size_t usedAfter = getUsedMem(); const size_t largeItemsAfter = getLargeItemsMem(); @@ -602,11 +630,11 @@ void MemoryManager::runGC() size_t MemoryManager::getUsedMem() const { size_t usedMem = 0; - for (QVector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) { + for (std::vector<PageAllocation>::const_iterator i = m_d->heapChunks.cbegin(), ei = m_d->heapChunks.cend(); i != ei; ++i) { Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(i->base()); for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) { Heap::Base *m = reinterpret_cast<Heap::Base *>(item); - Q_ASSERT((qintptr) item % 16 == 0); + Q_ASSERT(qintptr(item) % 16 == 0); if (m->inUse()) usedMem += header->itemSize; } @@ -617,7 +645,7 @@ size_t MemoryManager::getUsedMem() const size_t MemoryManager::getAllocatedMem() const { size_t total = 0; - for (int i = 0; i < m_d->heapChunks.size(); ++i) + for (size_t i = 0; i < m_d->heapChunks.size(); ++i) total += m_d->heapChunks.at(i).size(); return total; } @@ -680,7 +708,7 @@ void MemoryManager::collectFromJSStack() const Value *v = engine->jsStackBase; Value *top = engine->jsStackTop; while (v < top) { - Managed *m = v->as<Managed>(); + Managed *m = v->managed(); if (m && m->inUse()) // Skip pointers to already freed objects, they are bogus as well m->mark(engine); |