Memory leak when passing data between C++ objects and QML
-
I have 2 custom C++ objects:
The first is a connection object, called Provider, that brings in new images and there is only one instance of this object. This object generates signals that contain data elements such as new images, Here is an example event from this object:
@//Provider.h
void newTopImageData(QByteArray data);
void newSideImageData(QByteArray data);@Provider.cpp then fires this signal like this:
@
newTopImageData(QByteArray(imageBuf, size));
@This object is instantiated in main.cpp and then used throughout the QML. I have not noticed problems with integers, strings, and such - possibly because they are small. However, I recently had to add a new type to bring in images from the provider.
void ProviderImage::setImageData(QByteArray data)
{
if (m_image) delete m_image;
m_image = new QImage();
m_image->loadFromData(data);
update(); //repaint
}
@There are multiple ProviderImages that are declared in QML. Below is a snippet:
//listen for router messages found in the global object router_interface
Connections{
target: provider
onNewTopImageData: {
providerImageTop.setImageData(data);
}
onNewSideImageData: {
providerImageSide.setImageData(data);
}
}ProviderImage { id: providerImageTop width: 400 height: 415 anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top } ProviderImage { id: providerImageSide width: 200 height: 400 anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top }
@
I am able to get all the images to load correctly and have them updating about once per second. However, I am noticing that the application memory goes up with each update. I have tried several combinations of things and it seems like the leak is being caused by the data passed between C++ and QML/Javascript. I can make the memory leak go away by explicitly calling data's destructor in setImageData() but this causes the application to become unstable.
Any thoughts on better approaches or ways to fix this issue?
-
If you manually call gc() every so often, does the memory get released?
I suspect that what is happening here is that a temporary JavaScript object is being created to reference the data when you use it in QML, and that object is not being cleaned up until memory usage reaches the v8 high memory usage watermark. The QML2 engine still doesn't integrate too cleanly with v8's garbage collector, in that it doesn't include correct chunk size information when it allocates external data. Thus the garbage collector probably thinks that each of the temporary objects is only, say, 16 bytes, when really it is referencing a QByteArray which is holding much more data.
If you run valgrind --leak-check=full with your application, does an actual leak occur, or does it get cleaned up correctly on application teardown?
Cheers,
Chris. -
Dear Chris,
sorry for posting a reply so late.
We still have this problem, but in the meantime we switched to Qt 5.1_win32_opengl and the problem persists.
I also checked for memory leaks (using Visual Leak Detector), but no, there are no memory leaks reported. The memory is consumed at runtime and cleared at program end.
Cheers,
Bruno -
Are you talking dirty memory here? Calling gc() will cause the garbage collector to free those objects, but the JavaScript object memory will be released back to the JavaScript managed heap, rather than the operating system. If you can get detailed dirty memory usage information, that might shed a light on what's going on. If it peaks at some amount of memory after the first series of allocations, and then after subsequent gc() and further allocations, still retains the same memory usage level, then I expect that this is what is going on (as it will show that JS is "reusing" the memory which was originally allocated and then "freed" back to the JS heap, instead of sbrk'ing more memory from the OS).
I don't develop under windows, but on nix systems using pmap -x or looking at the smaps info would be useful.
If memory usage doesn't stabilise, then perhaps the QtQuick pixmap cache is involved here somehow (although I don't remember much about how that cache implementation works any more).
Cheers,
Chris. -
Our embedded application is deployed on an Angstrom distribution limited to 256MB of memory. The longer the application runs, the more memory that is consumed (several inputs require screen updates at least once a second). The issue is that the garbage collector would not run before the OS starts killing processes in an effort to free memory. As mentioned in a previous post, adding calls to gc() does provide a functional workaround. I'm not overly enamored with this solution. Is there a way to configure the garbage collector watermark?
I'm providing a gross example which causes a memory increase large enough to trigger garbage collection in a matter of minutes running on a PC vs. a day or so running our application. It also shows the way data is being passed from c++ to QML in our application (and no, not at 1000 times per second, example purposes only). Perhaps someone can provide an alternate approach.
@//
// ApplicationData.h
//
#ifndef APPLICATIONDATA_H
#define APPLICATIONDATA_H#include <QtGui>
class ApplicationData : public QObject
{
Q_OBJECTpublic:
ApplicationData();
Q_INVOKABLE QString getTemperature() const {
return temperature;
}public slots:
void update();signals:
void temperatureChanged();private:
QString temperature;
};#endif // APPLICATIONDATA_H@
@#include "ApplicationData.h"
ApplicationData::ApplicationData()
{
// timer used to quickly demonstrate memory usage
QTimer *timer = new QTimer(this);
timer->start(1);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
}//
// ApplicationData.cpp
//
void ApplicationData::update()
{
temperature = "00000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000011111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999";emit temperatureChanged();
}
@@//
// main.cpp
//
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>#include <ApplicationData.h>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);ApplicationData data; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("applicationData", &data); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec();
}
@@//
// main.qml
//
import QtQuick 2.3
import QtQuick.Controls 1.2ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Memory Example")Text { id: temperature text: applicationData.getTemperature() Connections { target: applicationData onTemperatureChanged: { temperature.text = applicationData.getTemperature() } } }
}
@Thanks in advance for any input.
Tim
-
Our embedded application is deployed on an Angstrom distribution limited to 256MB of memory. The longer the application runs, the more memory that is consumed (several inputs require screen updates at least once a second). The issue is that the garbage collector would not run before the OS starts killing processes in an effort to free memory. As mentioned in a previous post, adding calls to gc() does provide a functional workaround. I'm not overly enamored with this solution. Is there a way to configure the garbage collector watermark?
I'm providing a gross example which causes a memory increase large enough to trigger garbage collection in a matter of minutes running on a PC vs. a day or so running our application. It also shows the way data is being passed from c++ to QML in our application (and no, not at 1000 times per second, example purposes only). Perhaps someone can provide an alternate approach.
@//
// ApplicationData.h
//
#ifndef APPLICATIONDATA_H
#define APPLICATIONDATA_H#include <QtGui>
class ApplicationData : public QObject
{
Q_OBJECTpublic:
ApplicationData();
Q_INVOKABLE QString getTemperature() const {
return temperature;
}public slots:
void update();signals:
void temperatureChanged();private:
QString temperature;
};#endif // APPLICATIONDATA_H@
@#include "ApplicationData.h"
ApplicationData::ApplicationData()
{
// timer used to quickly demonstrate memory usage
QTimer *timer = new QTimer(this);
timer->start(1);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
}//
// ApplicationData.cpp
//
void ApplicationData::update()
{
temperature = "00000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999000000000011111111112222222222333333333344444444445555555555666666666677777777778888888888999999999900000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999";emit temperatureChanged();
}
@@//
// main.cpp
//
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>#include <ApplicationData.h>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);ApplicationData data; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("applicationData", &data); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec();
}
@@//
// main.qml
//
import QtQuick 2.3
import QtQuick.Controls 1.2ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Memory Example")Text { id: temperature text: applicationData.getTemperature() Connections { target: applicationData onTemperatureChanged: { temperature.text = applicationData.getTemperature() } } }
}
@Thanks in advance for any input.
Tim