Occasionally trim the type cache

As loaded components are kept in a cache, they are never removed by the
garbage collector. So, if you periodically create new components, they
leak. This change adds a floating threshold for the number of
components. When that threshold is surpassed trimCache() is called and
unneeded components are removed.

Task-number: QTBUG-42055
Change-Id: I30e3e4ee287f6d34376713668009c67614a50e0c
Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
This commit is contained in:
Ulf Hermann 2016-04-18 15:14:26 +02:00
parent cf28f909da
commit 23e0e26ce6
4 changed files with 59 additions and 1 deletions

View File

@ -1113,6 +1113,7 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
}
#define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
#define TYPELOADER_MINIMUM_TRIM_THRESHOLD 64
void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply)
{
@ -1592,7 +1593,8 @@ bool QQmlTypeLoader::QmldirContent::designerSupported() const
Constructs a new type loader that uses the given \a engine.
*/
QQmlTypeLoader::QQmlTypeLoader(QQmlEngine *engine)
: m_engine(engine), m_thread(new QQmlTypeLoaderThread(this))
: m_engine(engine), m_thread(new QQmlTypeLoaderThread(this)),
m_typeCacheTrimThreshold(TYPELOADER_MINIMUM_TRIM_THRESHOLD)
{
}
@ -1629,6 +1631,10 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &url, Mode mode)
QQmlTypeData *typeData = m_typeCache.value(url);
if (!typeData) {
// Trim before adding the new type, so that we don't immediately trim it away
if (m_typeCache.size() >= m_typeCacheTrimThreshold)
trimCache();
typeData = new QQmlTypeData(url, this);
// TODO: if (compiledData == 0), is it safe to omit this insertion?
m_typeCache.insert(url, typeData);
@ -1933,12 +1939,22 @@ void QQmlTypeLoader::clearCache()
qDeleteAll(m_importQmlDirCache);
m_typeCache.clear();
m_typeCacheTrimThreshold = TYPELOADER_MINIMUM_TRIM_THRESHOLD;
m_scriptCache.clear();
m_qmldirCache.clear();
m_importDirCache.clear();
m_importQmlDirCache.clear();
}
void QQmlTypeLoader::updateTypeCacheTrimThreshold()
{
int size = m_typeCache.size();
if (size > m_typeCacheTrimThreshold)
m_typeCacheTrimThreshold = size * 2;
if (size < m_typeCacheTrimThreshold / 2)
m_typeCacheTrimThreshold = qMax(size * 2, TYPELOADER_MINIMUM_TRIM_THRESHOLD);
}
void QQmlTypeLoader::trimCache()
{
while (true) {
@ -1963,6 +1979,8 @@ void QQmlTypeLoader::trimCache()
}
}
updateTypeCacheTrimThreshold();
// TODO: release any scripts which are no longer referenced by any types
}

View File

@ -358,6 +358,7 @@ private:
QQmlTypeLoaderThread *m_thread;
NetworkReplies m_networkReplies;
TypeCache m_typeCache;
int m_typeCacheTrimThreshold;
ScriptCache m_scriptCache;
QmldirCache m_qmldirCache;
ImportDirCache m_importDirCache;
@ -365,6 +366,7 @@ private:
template<typename Loader>
void doLoad(const Loader &loader, QQmlDataBlob *blob, Mode mode);
void updateTypeCacheTrimThreshold();
friend struct PlainLoader;
friend struct CachedLoader;

View File

@ -0,0 +1,8 @@
import QtQuick 2.2
Rectangle
{
objectName: "dings"
width: 100
height: 100
color: "blue"
}

View File

@ -35,6 +35,9 @@
#include <QtQml/qqmlengine.h>
#include <QtQuick/qquickview.h>
#include <QtQuick/qquickitem.h>
#include <QtQml/private/qqmlengine_p.h>
#include <QtQml/private/qqmltypeloader_p.h>
#include <QtQml/private/qqmlcompiler_p.h>
#include "../../shared/util.h"
class tst_QQMLTypeLoader : public QQmlDataTest
@ -44,6 +47,7 @@ class tst_QQMLTypeLoader : public QQmlDataTest
private slots:
void testLoadComplete();
void loadComponentSynchronously();
void trimCache();
};
void tst_QQMLTypeLoader::testLoadComplete()
@ -73,6 +77,32 @@ void tst_QQMLTypeLoader::loadComponentSynchronously()
QVERIFY(o);
}
void tst_QQMLTypeLoader::trimCache()
{
QQmlEngine engine;
QQmlTypeLoader &loader = QQmlEnginePrivate::get(&engine)->typeLoader;
for (int i = 0; i < 256; ++i) {
QUrl url = testFileUrl("trim_cache.qml");
url.setQuery(QString::number(i));
QQmlTypeData *data = loader.getType(url);
if (i % 5 == 0) // keep references to some of them so that they aren't trimmed
data->compiledData()->addref();
data->release();
}
for (int i = 0; i < 256; ++i) {
QUrl url = testFileUrl("trim_cache.qml");
url.setQuery(QString::number(i));
if (i % 5 == 0)
QVERIFY(loader.isTypeLoaded(url));
else if (i < 128)
QVERIFY(!loader.isTypeLoaded(url));
// The cache is free to keep the others.
}
}
QTEST_MAIN(tst_QQMLTypeLoader)
#include "tst_qqmltypeloader.moc"