Memory Allocation
-
The situation I have is a mystery memory allocation problem with Qt 4.8 specifically in the QDeclarativeView. Before commenting that the solution is in Qt5 please know we are stuck with this version of Qt so that is not a solution for us. What I've seen is that a bunch of allocated QDeclarativeView's are being updated in a loop that goes for awhile. These are pretty complex views (thus why we chose QML, it was ideal for producing detailed views that looked good).
What I'm observing is that inside of this loop memory is continually being allocated at a pretty substantial rate. I've even gone so far as to put a breakpoint into the new.cpp file and watch what is being allocated (when above a certain threshold because obviously there are always small allocations). The only code I've observed that does allocations in a large chunk is the QRasterPaintEngine.
The questions I have are: 1) Is this normal for a QDeclarativeView to request more and more memory? 2) Is the memory being released? 3) If the memory isn't being released, is there a way I can trigger Qt to release the memory? 4) If nothing else, is there a decent website that shows exactly how Qt memory allocation works and how to get the thing to release the memory?
Thanks everyone in advance.
-
Hi and welcome to devnet,
Doesn't sound normal however I don't know the internals of this part of Qt. Anyhow, can you reproduce this memory allocation behavior with a minimal compilable example ?
Also, which version of Qt 4 are you using ?
-
Sorry in advance for the length of this, 6000 characters doesn't seem like a lot.
I'm using 4.8.6. I think I've rationalized my issue- but I will still post some messy code. What I think we're seeing is that allocations occur very quickly and frequently, more so than deallocations. Eventually I think it would plateau and stabilize- however due to the number of declarative views we're using this is not an option. I took your advice and wrote a sample program; I should have done this earlier because it allowed me to experiment with some more parts of the API. Here's the C++ (called main.cpp):
@#include <QApplication>
#include <QDateTime>
#include <QDebug>
#include <QDeclarativeContext>
#include <QDeclarativeEngine>
#include <QDeclarativeView>
#include <QLabel>
#include <QObject>
#include <QPixmapCache>
#include <QThread>#include <cstdlib>
enum { TIME_THRESHOLD = 0 };
class Model : public QObject
{
Q_OBJECT
public:
Model() : m_break(false) { }void setColor(QString const& newColor)
{
Q_EMIT colorChanged(newColor);
}void setText(QString const& newText)
{
Q_EMIT textChanged(newText);
}bool eventFilter(QObject* watched, QEvent* evt)
{
if (evt->type() == QEvent::DeferredDelete)
{
qDebug() << "Got one.";
}
return false;
}public Q_SLOTS:
void setBreak(bool b) { m_break = b; }
bool shouldBreak() const { return m_break; }Q_SIGNALS:
void colorChanged(QString color);
void textChanged(QString text);private:
bool m_break;
};class T : public QThread
{
public:
static void msleep(int msecs) { QThread::msleep(msecs); }
};qint64 currentTime() { return QDateTime::currentMSecsSinceEpoch(); }
QString randomColor()
{
QColor color( rand() % 255, rand() % 255, rand() % 255);
return color.name();
}QString randomText()
{
int s = rand() % 5;
switch(s)
{
case 0: return "Hello World";
case 1: return "Foo Bar";
case 2: return "Goodbye World";
default:
return QString("Random Number %1").arg(QString::number(rand()));
};
}int main(int argc, char** argv)
{
QApplication app(argc, argv);Model model;
app.installEventFilter(&model);QDeclarativeView view;
QDeclarativeContext* context = view.rootContext();
context->setContextProperty("model", &model);
QDeclarativeEngine::setObjectOwnership( &model, QDeclarativeEngine::CppOwnership);view.setSource( QUrl::fromLocalFile("main.qml") );
view.setVisible(true);qint64 t = currentTime();
while(false == model.shouldBreak())
{
QApplication::processEvents();
view.engine()->clearComponentCache();
// QPixmapCache::clear();if (currentTime() >= t + TIME_THRESHOLD)
{
model.setColor(randomColor());
model.setText(randomText());
t = currentTime();
}
}
}#include "main.moc"
@Keep in mind the above example has been butchered up and down, so please forgive the sloppiness.
-
The QML is:
@
import QtQuick 1.1;Rectangle {
id: rootwidth: 500;
height: 500;color: "white";
Rectangle {
id: smallRectangle;
anchors.centerIn: parent;
width: 50;
height: 50;
}Rectangle {
id: outputTextBorder
anchors.centerIn: outputText
color: smallRectangle.color;
}Text {
id: outputText
anchors { left: parent.left; top: parent.top; margins: 30 }
onTextChanged: {
outputTextBorder.width = outputText.paintedWidth + 10;
outputTextBorder.height = outputText.paintedHeight + 3;
}
}Text {
id: outputText2
anchors { right: parent.right; top: parent.top; margins: 30 }
}Text {
id: outputText3
anchors { right: parent.right; bottom: parent.bottom; margins: 30 }
}Text {
id: outputText4
anchors { left: parent.left; bottom: parent.bottom; margins: 30 }
}Loader {
id: loader
anchors.verticalCenter: smallRectangle.verticalCenter;
anchors.left: smallRectangle.right;
width: 100;
height: 100;
sourceComponent: theSource
}Component {
id: theSource
Rectangle {
anchors.fill: parent
border.width: 2
radius: 5
border.color: "blue"function setColor(c) {
border.color = c;
innerText2.color = innerText.color;
innerText.color = c;
}function setText(t) { innerText.text = t; }
function setText2(t2) { innerText2.text = t2; }Text {
id: innerText
height: 30
anchors.centerIn: parent
color: "black"
}Text {
id: innerText2
anchors.top: innerText.bottom;
anchors.horizontalCenter: innerText.horizontalCenter;
color: "black"
}
}
}Connections {
target: modelonColorChanged: {
smallRectangle.color = color;
outputTextBorder.color = color;
loader.item.setColor(color);
gc();
}onTextChanged: {
outputText4.text = outputText3.text;
outputText3.text = outputText2.text;
outputText2.text = outputText.text;
outputText.text = text;
loader.item.setText(text);
loader.item.setText2(outputText3.text);
gc();
}
}
}@
The QML is also quite messy. I was trying to figure out what components/actions in QML exacerbate the frequency in allocations. It seems that Loaders really make this thing increase in size. When I run the program alongside task manager, it starts out at 5K which is what I would expect once loaded. However memory keeps increasing until it reached about 30K (took about a minute to two minutes to plateau). This is what I think I'm seeing in our production application only we are never able to reach the plateau because we run out of memory.
An interesting exercise was to see if I could keep the memory from climbing from 5K to 30K. The way I finally found of achieving this was to clear the QPixmapCache. With this clearing, the result was the memory size would increase from 5 to 7, then back to 5. It was exactly what we needed.
I'm not convinced that clearing the QPixmapCache is going to make the difference. The reason is because with multiple declarative views, only so much would fit in the cache anyways before it would start deallocating pixmaps. I did find an interesting thread that does match what I'm seeing:
"http://qt-project.org/forums/viewthread/14492":http://qt-project.org/forums/viewthread/14492
Thanks again for the help.