Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Memory leak when passing data between C++ objects and QML
QtWS25 Last Chance

Memory leak when passing data between C++ objects and QML

Scheduled Pinned Locked Moved QML and Qt Quick
7 Posts 4 Posters 6.4k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R Offline
    R Offline
    richterX
    wrote on last edited by
    #1

    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.

    @//ProviderImage.h

    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:

    @//View.qml

    //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?

    1 Reply Last reply
    0
    • B Offline
      B Offline
      bwilli
      wrote on last edited by
      #2

      Hi,

      as I'm facing the same problem (QtQuick 2, QT 5.0.1) - did you ever get a solution for your problem?

      Thanks,
      Bruno

      1 Reply Last reply
      0
      • C Offline
        C Offline
        chrisadams
        wrote on last edited by
        #3

        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.

        1 Reply Last reply
        0
        • B Offline
          B Offline
          bwilli
          wrote on last edited by
          #4

          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

          1 Reply Last reply
          0
          • C Offline
            C Offline
            chrisadams
            wrote on last edited by
            #5

            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.

            1 Reply Last reply
            0
            • T Offline
              T Offline
              tnt8593
              wrote on last edited by
              #6

              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_OBJECT

              public:
              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.2

              ApplicationWindow {
              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

              1 Reply Last reply
              0
              • T Offline
                T Offline
                tnt8593
                wrote on last edited by
                #7

                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_OBJECT

                public:
                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.2

                ApplicationWindow {
                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

                1 Reply Last reply
                0

                • Login

                • Login or register to search.
                • First post
                  Last post
                0
                • Categories
                • Recent
                • Tags
                • Popular
                • Users
                • Groups
                • Search
                • Get Qt Extensions
                • Unsolved