aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/memory
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/memory')
-rw-r--r--src/qml/memory/memory.pri14
-rw-r--r--src/qml/memory/qv4heap_p.h109
-rw-r--r--src/qml/memory/qv4mm.cpp617
-rw-r--r--src/qml/memory/qv4mm_p.h187
4 files changed, 927 insertions, 0 deletions
diff --git a/src/qml/memory/memory.pri b/src/qml/memory/memory.pri
new file mode 100644
index 0000000000..04b7566ccc
--- /dev/null
+++ b/src/qml/memory/memory.pri
@@ -0,0 +1,14 @@
+INCLUDEPATH += $$PWD
+INCLUDEPATH += $$OUT_PWD
+
+!qmldevtools_build {
+SOURCES += \
+ $$PWD/qv4mm.cpp \
+
+HEADERS += \
+ $$PWD/qv4mm_p.h
+
+}
+
+HEADERS += \
+ $$PWD/qv4heap_p.h
diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h
new file mode 100644
index 0000000000..96d73766d0
--- /dev/null
+++ b/src/qml/memory/qv4heap_p.h
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4HEAP_P_H
+#define QV4HEAP_P_H
+
+#include <QtCore/QString>
+#include <private/qv4global_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+struct Q_QML_EXPORT Base {
+ union {
+ const ManagedVTable *vtable;
+ quintptr mm_data;
+ };
+
+ inline ReturnedValue asReturnedValue() const;
+ inline void mark(QV4::ExecutionEngine *engine);
+
+ enum {
+ MarkBit = 0x1,
+ NotInUse = 0x2,
+ PointerMask = ~0x3
+ };
+
+ ManagedVTable *gcGetVtable() const {
+ return reinterpret_cast<ManagedVTable *>(mm_data & PointerMask);
+ }
+ inline bool isMarked() const {
+ return mm_data & MarkBit;
+ }
+ inline void setMarkBit() {
+ mm_data |= MarkBit;
+ }
+ inline void clearMarkBit() {
+ mm_data &= ~MarkBit;
+ }
+
+ inline bool inUse() const {
+ return !(mm_data & NotInUse);
+ }
+
+ Base *nextFree() {
+ return reinterpret_cast<Base *>(mm_data & PointerMask);
+ }
+ void setNextFree(Base *m) {
+ mm_data = (reinterpret_cast<quintptr>(m) | NotInUse);
+ }
+
+ void *operator new(size_t, Managed *m) { return m; }
+ void *operator new(size_t, Heap::Base *m) { return m; }
+ void operator delete(void *, Heap::Base *) {}
+};
+
+template <typename T>
+struct Pointer {
+ Pointer() {}
+ Pointer(T *t) : ptr(t) {}
+
+ T *operator->() const { return static_cast<T *>(ptr); }
+ operator T *() const { return static_cast<T *>(ptr); }
+
+ Pointer &operator =(T *t) { ptr = t; return *this; }
+
+ // Use Base, not T here, to ensure T inherits from ptr
+ Base *ptr;
+};
+
+}
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
new file mode 100644
index 0000000000..07408a343c
--- /dev/null
+++ b/src/qml/memory/qv4mm.cpp
@@ -0,0 +1,617 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4engine_p.h"
+#include "qv4object_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4mm_p.h"
+#include "qv4qobjectwrapper_p.h"
+#include <qqmlengine.h>
+#include "PageAllocation.h"
+#include "StdLibExtras.h"
+
+#include <QTime>
+#include <QVector>
+#include <QVector>
+#include <QMap>
+
+#include <iostream>
+#include <cstdlib>
+#include <algorithm>
+#include "qv4alloca_p.h"
+#include "qv4profiling_p.h"
+
+#ifdef V4_USE_VALGRIND
+#include <valgrind/valgrind.h>
+#include <valgrind/memcheck.h>
+#endif
+
+#if OS(QNX)
+#include <sys/storage.h> // __tls()
+#endif
+
+#if USE(PTHREADS) && HAVE(PTHREAD_NP_H)
+#include <pthread_np.h>
+#endif
+
+using namespace WTF;
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+struct MemoryManager::Data
+{
+ struct ChunkHeader {
+ Heap::Base freeItems;
+ ChunkHeader *nextNonFull;
+ char *itemStart;
+ char *itemEnd;
+ int 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;
+
+ struct LargeItem {
+ LargeItem *next;
+ size_t size;
+ void *data;
+
+ Heap::Base *heapObject() {
+ return reinterpret_cast<Heap::Base *>(&data);
+ }
+ };
+
+ LargeItem *largeItems;
+ std::size_t totalLargeItemsAllocated;
+
+ GCDeletable *deletable;
+
+ // statistics:
+#ifdef DETAILED_MM_STATS
+ QVector<unsigned> allocSizeCounters;
+#endif // DETAILED_MM_STATS
+
+ Data()
+ : gcBlocked(false)
+ , engine(0)
+ , totalItems(0)
+ , totalAlloc(0)
+ , maxShift(6)
+ , maxChunkSize(32*1024)
+ , largeItems(0)
+ , totalLargeItemsAllocated(0)
+ , deletable(0)
+ {
+ memset(nonFullChunks, 0, sizeof(nonFullChunks));
+ memset(nChunks, 0, sizeof(nChunks));
+ memset(availableItems, 0, sizeof(availableItems));
+ memset(allocCount, 0, sizeof(allocCount));
+ aggressiveGC = !qgetenv("QV4_MM_AGGRESSIVE_GC").isEmpty();
+ gcStats = !qgetenv("QV4_MM_STATS").isEmpty();
+
+ QByteArray overrideMaxShift = qgetenv("QV4_MM_MAXBLOCK_SHIFT");
+ bool ok;
+ uint override = overrideMaxShift.toUInt(&ok);
+ if (ok && override <= 11 && override > 0)
+ maxShift = override;
+
+ QByteArray maxChunkString = qgetenv("QV4_MM_MAX_CHUNK_SIZE");
+ std::size_t tmpMaxChunkSize = maxChunkString.toUInt(&ok);
+ if (ok)
+ maxChunkSize = tmpMaxChunkSize;
+ }
+
+ ~Data()
+ {
+ for (QVector<PageAllocation>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) {
+ Q_V4_PROFILE_DEALLOC(engine, 0, i->size(), Profiling::HeapPage);
+ i->deallocate();
+ }
+ }
+};
+
+namespace {
+
+bool sweepChunk(MemoryManager::Data::ChunkHeader *header, uint *itemsInUse, ExecutionEngine *engine)
+{
+ bool isEmpty = true;
+ Heap::Base *tail = &header->freeItems;
+// qDebug("chunkStart @ %p, size=%x, pos=%x", header->itemStart, header->itemSize, header->itemSize>>4);
+#ifdef V4_USE_VALGRIND
+ VALGRIND_DISABLE_ERROR_REPORTING;
+#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"));
+
+ Q_ASSERT((qintptr) item % 16 == 0);
+
+ if (m->isMarked()) {
+ Q_ASSERT(m->inUse());
+ m->clearMarkBit();
+ isEmpty = false;
+ ++(*itemsInUse);
+ } else {
+ if (m->inUse()) {
+// qDebug() << "-- collecting it." << m << tail << m->nextFree();
+#ifdef V4_USE_VALGRIND
+ VALGRIND_ENABLE_ERROR_REPORTING;
+#endif
+ if (m->gcGetVtable()->destroy)
+ m->gcGetVtable()->destroy(m);
+
+ memset(m, 0, header->itemSize);
+#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);
+ ++(*itemsInUse);
+ }
+ // Relink all free blocks to rewrite references to any released chunk.
+ tail->setNextFree(m);
+ tail = m;
+ }
+ }
+ tail->setNextFree(0);
+#ifdef V4_USE_VALGRIND
+ VALGRIND_ENABLE_ERROR_REPORTING;
+#endif
+ return isEmpty;
+}
+
+} // namespace
+
+MemoryManager::MemoryManager(ExecutionEngine *engine)
+ : m_d(new Data)
+ , m_persistentValues(new PersistentValueStorage(engine))
+ , m_weakValues(new PersistentValueStorage(engine))
+{
+#ifdef V4_USE_VALGRIND
+ VALGRIND_CREATE_MEMPOOL(this, 0, true);
+#endif
+ m_d->engine = engine;
+}
+
+Heap::Base *MemoryManager::allocData(std::size_t size)
+{
+ if (m_d->aggressiveGC)
+ runGC();
+#ifdef DETAILED_MM_STATS
+ willAllocate(size);
+#endif // DETAILED_MM_STATS
+
+ Q_ASSERT(size >= 16);
+ Q_ASSERT(size % 16 == 0);
+
+ size_t pos = size >> 4;
+
+ // doesn't fit into a small bucket
+ if (size >= MemoryManager::Data::MaxItemSize) {
+ if (m_d->totalLargeItemsAllocated > 8 * 1024 * 1024)
+ runGC();
+
+ // we use malloc for this
+ MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>(
+ malloc(Q_V4_PROFILE_ALLOC(m_d->engine, size + sizeof(MemoryManager::Data::LargeItem),
+ Profiling::LargeItem)));
+ memset(item, 0, size + sizeof(MemoryManager::Data::LargeItem));
+ item->next = m_d->largeItems;
+ item->size = size;
+ m_d->largeItems = item;
+ m_d->totalLargeItemsAllocated += size;
+ return item->heapObject();
+ }
+
+ Heap::Base *m = 0;
+ Data::ChunkHeader *header = m_d->nonFullChunks[pos];
+ if (header) {
+ m = header->freeItems.nextFree();
+ goto found;
+ }
+
+ // 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) {
+ runGC();
+ header = m_d->nonFullChunks[pos];
+ if (header) {
+ m = header->freeItems.nextFree();
+ goto found;
+ }
+ }
+
+ // no free item available, allocate a new chunk
+ {
+ // allocate larger chunks at a time to avoid excessive GC, but cap at maximum chunk size (2MB by default)
+ uint shift = ++m_d->nChunks[pos];
+ 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(m_d->engine, allocSize, Profiling::HeapPage),
+ OSAllocator::JSGCHeapPages);
+ m_d->heapChunks.append(allocation);
+ std::sort(m_d->heapChunks.begin(), m_d->heapChunks.end());
+
+ header = reinterpret_cast<Data::ChunkHeader *>(allocation.base());
+ header->itemSize = int(size);
+ header->itemStart = reinterpret_cast<char *>(allocation.base()) + roundUpToMultipleOf(16, sizeof(Data::ChunkHeader));
+ header->itemEnd = reinterpret_cast<char *>(allocation.base()) + allocation.size() - header->itemSize;
+
+ header->nextNonFull = m_d->nonFullChunks[pos];
+ m_d->nonFullChunks[pos] = header;
+
+ Heap::Base *last = &header->freeItems;
+ for (char *item = header->itemStart; item <= header->itemEnd; item += header->itemSize) {
+ Heap::Base *o = reinterpret_cast<Heap::Base *>(item);
+ last->setNextFree(o);
+ last = o;
+
+ }
+ last->setNextFree(0);
+ m = header->freeItems.nextFree();
+ const size_t increase = (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
+ }
+
+ found:
+#ifdef V4_USE_VALGRIND
+ VALGRIND_MEMPOOL_ALLOC(this, m, size);
+#endif
+ Q_V4_PROFILE_ALLOC(m_d->engine, size, Profiling::SmallItem);
+
+ ++m_d->allocCount[pos];
+ ++m_d->totalAlloc;
+ header->freeItems.setNextFree(m->nextFree());
+ if (!header->freeItems.nextFree())
+ m_d->nonFullChunks[pos] = header->nextNonFull;
+ return m;
+}
+
+static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase)
+{
+ while (engine->jsStackTop > markBase) {
+ Heap::Base *h = engine->popForGC();
+ Q_ASSERT (h->gcGetVtable()->markObjects);
+ h->gcGetVtable()->markObjects(h, engine);
+ }
+}
+
+void MemoryManager::mark()
+{
+ Value *markBase = m_d->engine->jsStackTop;
+
+ m_d->engine->markObjects();
+
+ m_persistentValues->mark(m_d->engine);
+
+ collectFromJSStack();
+
+ // Preserve QObject ownership rules within JavaScript: A parent with c++ ownership
+ // keeps all of its children alive in JavaScript.
+
+ // Do this _after_ collectFromStack to ensure that processing the weak
+ // managed objects in the loop down there doesn't make then end up as leftovers
+ // on the stack and thus always get collected.
+ for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
+ if (!(*it).isManaged())
+ continue;
+ if ((*it).managed()->d()->gcGetVtable() != QObjectWrapper::staticVTable())
+ continue;
+ QObjectWrapper *qobjectWrapper = static_cast<QObjectWrapper*>((*it).managed());
+ if (!qobjectWrapper)
+ continue;
+ QObject *qobject = qobjectWrapper->object();
+ if (!qobject)
+ continue;
+ bool keepAlive = QQmlData::keepAliveDuringGarbageCollection(qobject);
+
+ if (!keepAlive) {
+ if (QObject *parent = qobject->parent()) {
+ while (parent->parent())
+ parent = parent->parent();
+
+ keepAlive = QQmlData::keepAliveDuringGarbageCollection(parent);
+ }
+ }
+
+ if (keepAlive)
+ qobjectWrapper->mark(m_d->engine);
+
+ if (m_d->engine->jsStackTop >= m_d->engine->jsStackLimit)
+ drainMarkStack(m_d->engine, markBase);
+ }
+
+ drainMarkStack(m_d->engine, markBase);
+}
+
+void MemoryManager::sweep(bool lastSweep)
+{
+ if (m_weakValues) {
+ for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) {
+ if (Managed *m = (*it).asManaged()) {
+ if (!m->markBit())
+ (*it) = Primitive::undefinedValue();
+ }
+ }
+ }
+
+ if (MultiplyWrappedQObjectMap *multiplyWrappedQObjects = m_d->engine->m_multiplyWrappedQObjects) {
+ for (MultiplyWrappedQObjectMap::Iterator it = multiplyWrappedQObjects->begin(); it != multiplyWrappedQObjects->end();) {
+ if (!it.value()->markBit())
+ it = multiplyWrappedQObjects->erase(it);
+ else
+ ++it;
+ }
+ }
+
+ bool *chunkIsEmpty = (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) {
+ Data::ChunkHeader *header = reinterpret_cast<Data::ChunkHeader *>(m_d->heapChunks[i].base());
+ chunkIsEmpty[i] = sweepChunk(header, &itemsInUse[header->itemSize >> 4], m_d->engine);
+ }
+
+ QVector<PageAllocation>::iterator chunkIter = m_d->heapChunks.begin();
+ for (int 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;
+
+ // 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(m_d->engine, 0, chunkIter->size(), Profiling::HeapPage);
+#ifdef V4_USE_VALGRIND
+ VALGRIND_MEMPOOL_FREE(this, header);
+#endif
+ --m_d->nChunks[pos];
+ m_d->availableItems[pos] -= uint(decrease);
+ m_d->totalItems -= int(decrease);
+ chunkIter->deallocate();
+ chunkIter = m_d->heapChunks.erase(chunkIter);
+ continue;
+ } else if (header->freeItems.nextFree()) {
+ header->nextNonFull = m_d->nonFullChunks[pos];
+ m_d->nonFullChunks[pos] = header;
+ }
+ ++chunkIter;
+ }
+
+ Data::LargeItem *i = m_d->largeItems;
+ Data::LargeItem **last = &m_d->largeItems;
+ while (i) {
+ Heap::Base *m = i->heapObject();
+ Q_ASSERT(m->inUse());
+ if (m->isMarked()) {
+ m->clearMarkBit();
+ last = &i->next;
+ i = i->next;
+ continue;
+ }
+ if (m->gcGetVtable()->destroy)
+ m->gcGetVtable()->destroy(m);
+
+ *last = i->next;
+ free(Q_V4_PROFILE_DEALLOC(m_d->engine, i, i->size + sizeof(Data::LargeItem),
+ Profiling::LargeItem));
+ i = *last;
+ }
+
+ GCDeletable *deletable = m_d->deletable;
+ m_d->deletable = 0;
+ while (deletable) {
+ GCDeletable *next = deletable->next;
+ deletable->lastCall = lastSweep;
+ delete deletable;
+ deletable = next;
+ }
+
+ // some execution contexts are allocated on the stack, make sure we clear their markBit as well
+ if (!lastSweep) {
+ Heap::ExecutionContext *ctx = engine()->current;
+ while (ctx) {
+ ctx->clearMarkBit();
+ ctx = ctx->parent;
+ }
+ }
+}
+
+bool MemoryManager::isGCBlocked() const
+{
+ return m_d->gcBlocked;
+}
+
+void MemoryManager::setGCBlocked(bool blockGC)
+{
+ m_d->gcBlocked = blockGC;
+}
+
+void MemoryManager::runGC()
+{
+ if (m_d->gcBlocked) {
+// qDebug() << "Not running GC.";
+ return;
+ }
+
+ if (!m_d->gcStats) {
+ mark();
+ sweep();
+ } else {
+ const size_t totalMem = getAllocatedMem();
+
+ QTime t;
+ t.start();
+ mark();
+ int markTime = t.elapsed();
+ t.restart();
+ const size_t usedBefore = getUsedMem();
+ int chunksBefore = m_d->heapChunks.size();
+ sweep();
+ const size_t usedAfter = getUsedMem();
+ int sweepTime = t.elapsed();
+
+ qDebug() << "========== GC ==========";
+ qDebug() << "Marked object in" << markTime << "ms.";
+ qDebug() << "Sweeped object in" << sweepTime << "ms.";
+ qDebug() << "Allocated" << totalMem << "bytes in" << m_d->heapChunks.size() << "chunks.";
+ qDebug() << "Used memory before GC:" << usedBefore;
+ qDebug() << "Used memory after GC:" << usedAfter;
+ qDebug() << "Freed up bytes:" << (usedBefore - usedAfter);
+ qDebug() << "Released chunks:" << (chunksBefore - m_d->heapChunks.size());
+ qDebug() << "======== End GC ========";
+ }
+
+ memset(m_d->allocCount, 0, sizeof(m_d->allocCount));
+ m_d->totalAlloc = 0;
+ m_d->totalLargeItemsAllocated = 0;
+}
+
+size_t MemoryManager::getUsedMem() const
+{
+ size_t usedMem = 0;
+ for (QVector<PageAllocation>::const_iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); 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);
+ if (m->inUse())
+ usedMem += header->itemSize;
+ }
+ }
+ return usedMem;
+}
+
+size_t MemoryManager::getAllocatedMem() const
+{
+ size_t total = 0;
+ for (int i = 0; i < m_d->heapChunks.size(); ++i)
+ total += m_d->heapChunks.at(i).size();
+ return total;
+}
+
+size_t MemoryManager::getLargeItemsMem() const
+{
+ size_t total = 0;
+ for (const Data::LargeItem *i = m_d->largeItems; i != 0; i = i->next)
+ total += i->size;
+ return total;
+}
+
+MemoryManager::~MemoryManager()
+{
+ delete m_persistentValues;
+ delete m_weakValues;
+ m_weakValues = 0;
+
+ sweep(/*lastSweep*/true);
+#ifdef V4_USE_VALGRIND
+ VALGRIND_DESTROY_MEMPOOL(this);
+#endif
+}
+
+ExecutionEngine *MemoryManager::engine() const
+{
+ return m_d->engine;
+}
+
+void MemoryManager::dumpStats() const
+{
+#ifdef DETAILED_MM_STATS
+ std::cerr << "=================" << std::endl;
+ std::cerr << "Allocation stats:" << std::endl;
+ std::cerr << "Requests for each chunk size:" << std::endl;
+ for (int i = 0; i < m_d->allocSizeCounters.size(); ++i) {
+ if (unsigned count = m_d->allocSizeCounters[i]) {
+ std::cerr << "\t" << (i << 4) << " bytes chunks: " << count << std::endl;
+ }
+ }
+#endif // DETAILED_MM_STATS
+}
+
+void MemoryManager::registerDeletable(GCDeletable *d)
+{
+ d->next = m_d->deletable;
+ m_d->deletable = d;
+}
+
+#ifdef DETAILED_MM_STATS
+void MemoryManager::willAllocate(std::size_t size)
+{
+ unsigned alignedSize = (size + 15) >> 4;
+ QVector<unsigned> &counters = m_d->allocSizeCounters;
+ if ((unsigned) counters.size() < alignedSize + 1)
+ counters.resize(alignedSize + 1);
+ counters[alignedSize]++;
+}
+
+#endif // DETAILED_MM_STATS
+
+void MemoryManager::collectFromJSStack() const
+{
+ Value *v = m_d->engine->jsStackBase;
+ Value *top = m_d->engine->jsStackTop;
+ while (v < top) {
+ Managed *m = v->asManaged();
+ if (m && m->inUse())
+ // Skip pointers to already freed objects, they are bogus as well
+ m->mark(m_d->engine);
+ ++v;
+ }
+}
+QT_END_NAMESPACE
diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h
new file mode 100644
index 0000000000..422809ba54
--- /dev/null
+++ b/src/qml/memory/qv4mm_p.h
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4GC_H
+#define QV4GC_H
+
+#include <private/qv4global_p.h>
+#include <private/qv4value_p.h>
+#include <private/qv4scopedvalue_p.h>
+
+//#define DETAILED_MM_STATS
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct GCDeletable;
+
+class Q_QML_EXPORT MemoryManager
+{
+ Q_DISABLE_COPY(MemoryManager);
+
+public:
+ struct Data;
+
+ class GCBlocker
+ {
+ public:
+ GCBlocker(MemoryManager *mm)
+ : mm(mm)
+ , wasBlocked(mm->isGCBlocked())
+ {
+ mm->setGCBlocked(true);
+ }
+
+ ~GCBlocker()
+ {
+ mm->setGCBlocked(wasBlocked);
+ }
+
+ private:
+ MemoryManager *mm;
+ bool wasBlocked;
+ };
+
+public:
+ MemoryManager(ExecutionEngine *engine);
+ ~MemoryManager();
+
+ // TODO: this is only for 64bit (and x86 with SSE/AVX), so exend it for other architectures to be slightly more efficient (meaning, align on 8-byte boundaries).
+ // Note: all occurrences of "16" in alloc/dealloc are also due to the alignment.
+ static inline std::size_t align(std::size_t size)
+ { return (size + 15) & ~0xf; }
+
+ template<typename ManagedType>
+ inline typename ManagedType::Data *allocManaged(std::size_t size)
+ {
+ size = align(size);
+ Heap::Base *o = allocData(size);
+ o->vtable = ManagedType::staticVTable();
+ return static_cast<typename ManagedType::Data *>(o);
+ }
+
+ template <typename ManagedType>
+ typename ManagedType::Data *alloc()
+ {
+ Scope scope(engine());
+ Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
+ (void)new (t->d()) typename ManagedType::Data();
+ return t->d();
+ }
+
+ template <typename ManagedType, typename Arg1>
+ typename ManagedType::Data *alloc(Arg1 arg1)
+ {
+ Scope scope(engine());
+ Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
+ (void)new (t->d()) typename ManagedType::Data(arg1);
+ return t->d();
+ }
+
+ template <typename ManagedType, typename Arg1, typename Arg2>
+ typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2)
+ {
+ Scope scope(engine());
+ Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
+ (void)new (t->d()) typename ManagedType::Data(arg1, arg2);
+ return t->d();
+ }
+
+ template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3>
+ typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3)
+ {
+ Scope scope(engine());
+ Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
+ (void)new (t->d()) typename ManagedType::Data(arg1, arg2, arg3);
+ return t->d();
+ }
+
+ template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+ typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
+ {
+ Scope scope(engine());
+ Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
+ (void)new (t->d()) typename ManagedType::Data(arg1, arg2, arg3, arg4);
+ return t->d();
+ }
+
+ template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
+ typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5)
+ {
+ Scope scope(engine());
+ Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
+ (void)new (t->d()) typename ManagedType::Data(arg1, arg2, arg3, arg4, arg5);
+ return t->d();
+ }
+
+ bool isGCBlocked() const;
+ void setGCBlocked(bool blockGC);
+ void runGC();
+
+ ExecutionEngine *engine() const;
+
+ void dumpStats() const;
+
+ void registerDeletable(GCDeletable *d);
+
+ size_t getUsedMem() const;
+ size_t getAllocatedMem() const;
+ size_t getLargeItemsMem() const;
+
+protected:
+ /// expects size to be aligned
+ // TODO: try to inline
+ Heap::Base *allocData(std::size_t size);
+
+#ifdef DETAILED_MM_STATS
+ void willAllocate(std::size_t size);
+#endif // DETAILED_MM_STATS
+
+private:
+ void collectFromJSStack() const;
+ void mark();
+ void sweep(bool lastSweep = false);
+
+protected:
+ QScopedPointer<Data> m_d;
+public:
+ PersistentValueStorage *m_persistentValues;
+ PersistentValueStorage *m_weakValues;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4GC_H