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. QML application constantly increases in memory usage
Forum Updated to NodeBB v4.3 + New Features

QML application constantly increases in memory usage

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
9 Posts 2 Posters 3.8k Views 2 Watching
  • 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.
  • A Offline
    A Offline
    axel_s
    wrote on 23 Aug 2017, 08:55 last edited by
    #1

    Hi there,

    I have some problems to understand why my (simplified) logger application increases in memory consumption. I used heaptrack to measure allocations and to track down the source of memory consumption. Closing the application seems to free memory just before closing, so that heaptrack does not show this as a leak.

    The project is quite simple and based on QML with an own class derived by QAbstractTableModel providing the messages to a TableView.
    In this test setup, test messages are pushed to the model in a specific frequency. If the maximum number of messages is reached, old messages are deleted.

    Furthermore, I compiled the application using Visual Studio. Here the memory profiler gave a little bit more details about the allocations (see. screenshots attached).
    According to the memory consumption report, for each entry a QObjectPrivate::Connection and QtPrivate::QSlotObject is created. It seems to stay alive even after deleting the element.
    I have no idea, if this is as expected and why this function doesn't free connections if rows are deleted.

    Can anyone help me with this? I'd appreciate any suggestions! Thanks!

    Screenshots of Memory Profiler:
    0_1503477026290_Screenshot_Memory_usage_Logger1_1.PNG
    2_1503477026290_Screenshot_Memory_usage_Logger_1_2.PNG
    1_1503477026290_Screenshot_Memory_usage_Logger2.PNG

    In the following the most important source files (sorry for the long post, I was not allowed to upload zip files):

    main.qml:

    import QtQuick 2.0
    import QtQuick.Controls 1.4
    import QtQuick.Controls.Styles 1.4
    import QtQuick.Layouts 1.2
    
    TabView {
        id: view
        objectName: "tabView"
        anchors.fill: parent
        Layout.fillWidth: true
        Layout.fillHeight: true
        Layout.margins: 5
    
        tabPosition: 1
        clip: true
        style: TabViewStyle {
            tab: Item {}
        }
        Tab {
            title: "Debugger"
            Item {
                anchors.fill: parent
                anchors.margins: 20
                property string headerColour: "lightsteelblue"
    
                TableView {
                    id: messageView
                    model: logger.messageModel
    
                    property int defaultHeight: parent.height - 10
                    height:                   defaultHeight
                    anchors.horizontalCenter: parent.horizontalCenter
                    width:                    parent.width
    
                    itemDelegate: Text {
                        anchors.left: !!parent ? parent.left : undefined
                        anchors.leftMargin: 4
                        text:  styleData.value
                    }
    
                    TableViewColumn{title: qsTr("ID")       ; role: "identifier"   ; width:  30}
                    TableViewColumn{title: qsTr("Message")  ; role: "message"      ; width: 780} //  530
                }
            }
        }
    }
    
    

    Message.cpp:

    #include "Message.h"
    
    #include <QCoreApplication>
    
    int Message::nextID = 0;
    int Message::maxID  = 1e5;
    
    Message::Message()
        : id(++Message::nextID)
        , message()
    {
        if (nextID >= maxID) nextID = 0;
    }
    
    QString Message::print() const
    {
        return QString()
             + message + "\n"
             + QString("---") + "\n";
    }
    
    

    MessageDataModel.cpp:

    #include "MessageDataModel.h"
    
    #include <QtCore/QSet>
    
    #include <cmath>
    #include <set>
    #include <iostream>
    #include <thread>
    using std::cout;
    using std::cerr;
    using std::endl;
    using std::vector;
    using std::string;
    
    MessageDataModel::MessageDataModel(QObject *parent)
        : QAbstractTableModel(parent)
        , displayInOrder(true)
    {}
    
    int MessageDataModel::rowCount(const QModelIndex &parent) const
    {
        return messages.size();
    }
    
    int MessageDataModel::columnCount(const QModelIndex &parent) const
    {
        return Message::NumAttributes;
    }
    
    QHash<int, QByteArray> MessageDataModel::roleNames() const
    {
        return {
            {IDRole             , "identifier"    },
            {MessageRole        , "message"       },
        };
    }
    
    QVariant MessageDataModel::data(const QModelIndex &index, int role) const
    {
        if (index.row() < 0 || messages.size() <= index.row())
            return QVariant();
    
        const Message& msg = message(index.row());
    
        switch (role) {
        case IDRole             : return QString::number(msg.id);
        case MessageRole        : return msg.message;
        }
        return QVariant();
    }
    
    //=========== end of methods required by AbstractTableView ===============
    
    void MessageDataModel::pushRow(std::unique_ptr<Message> newMessage)
    {
        // reduce model (if necessary) before inserting elements
        enforceLimit();
    
        // insert new messages
        int insertStart = displayInOrder ? messages.size() : 0;
        beginInsertRows(QModelIndex(), insertStart, insertStart);
        messages.push_back(std::move(newMessage));
        endInsertRows();
    }
    
    // ========= private =========
    void MessageDataModel::enforceLimit()
    {
        int remaining = messageLimit;
        int currentSize = messages.size();
        if (currentSize > remaining) {
            remaining = int(remaining * (1.0 - deletePercentage/100.0)); // don't delete just enough, delete a little more
            int startIndex = displayInOrder ? 0 : remaining;
            beginRemoveRows(QModelIndex(), startIndex, startIndex+currentSize-remaining-1);
            messages.erase(messages.begin(),                          // first <N> messages are deleted (the oldest messages)
                           messages.end() - remaining);               //    where N = currentSize - remaining
            endRemoveRows();
        }
    }
    

    Test Logger (Logger.cpp):

    #include "Logger.h"
    
    #include <QFile>
    #include <QFileInfo>
    #include <QDebug>
    
    #include <string>
    
    
    // NON-STATIC
    Logger::Logger(QObject* parent)
        : QObject(parent)
        , _model()
    {
    
        connect(&_timer, &QTimer::timeout, this, &Logger::addDebugMessages);
        _timer.start(50);
    }
    
    Logger::~Logger()
    {
    }
    
    MessageDataModel* Logger::messageModel()
    {
        return &_model;
    }
    
    void Logger::addDebugMessages()
    {
        std::unique_ptr<Message> msg (new Message());
        msg->message   = QString("TestString").append(QString::number(msg->id));
        _model.pushRow(std::move(msg));
    
    }
    
    
    1 Reply Last reply
    0
    • S Offline
      S Offline
      SGaist
      Lifetime Qt Champion
      wrote on 23 Aug 2017, 20:44 last edited by
      #2

      Hi and welcome to devnet,

      Your call to erase only removes the pointer, it doesn't delete what's pointed to so you first have to delete the messages objects and then call erase on the vector.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      A 1 Reply Last reply 24 Aug 2017, 08:22
      0
      • S SGaist
        23 Aug 2017, 20:44

        Hi and welcome to devnet,

        Your call to erase only removes the pointer, it doesn't delete what's pointed to so you first have to delete the messages objects and then call erase on the vector.

        A Offline
        A Offline
        axel_s
        wrote on 24 Aug 2017, 08:22 last edited by
        #3

        Hi SGaist, thanks for your reply.
        I am pretty sure the Message objects are deleted because the elements we delete from the messages vector is a unique_ptr. I just tested this by adding a destructor to the Message class, each message gets destroyed actually..

        1 Reply Last reply
        0
        • A Offline
          A Offline
          axel_s
          wrote on 25 Aug 2017, 08:06 last edited by
          #4

          After doing some further testing I noticed, that memory consumption gets stable, if I use a ListView instead of TableView. I only exchanged the TableView { ... } with this:

          ListView {
                  id: messageView
                  model: logger.messageModel
          
                  property int defaultHeight: parent.height - 10
                  height:                   defaultHeight
                  anchors.horizontalCenter: parent.horizontalCenter
                  width:                    parent.width
          
                  delegate: Text {
                      anchors.left: !!parent ? parent.left : undefined
                      anchors.leftMargin: 4
                      text:  message
                  }
              }
          

          May this have something to do with the way I use TableView?

          1 Reply Last reply
          0
          • S Offline
            S Offline
            SGaist
            Lifetime Qt Champion
            wrote on 25 Aug 2017, 20:45 last edited by
            #5

            There might be some things here about performance.

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            1 Reply Last reply
            0
            • A Offline
              A Offline
              axel_s
              wrote on 31 Aug 2017, 14:33 last edited by
              #6

              Thanks, I had a look at this page already without any further insights.
              Even if there is the trade-off between memory and performance, I'd never expect some kind of caching for objects which have been removed from the model. An implementation of a logger for long-term applications would lead to huge memory consumption..

              1 Reply Last reply
              0
              • S Offline
                S Offline
                SGaist
                Lifetime Qt Champion
                wrote on 31 Aug 2017, 22:00 last edited by
                #7

                Which version of Qt are you using by the way ?

                Do you have the same results when running in release mode ?

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  axel_s
                  wrote on 5 Sept 2017, 08:43 last edited by
                  #8

                  Sorry, I forgot to mention I am using Qt 5.9.1 with same results in release mode.

                  1 Reply Last reply
                  0
                  • S Offline
                    S Offline
                    SGaist
                    Lifetime Qt Champion
                    wrote on 5 Sept 2017, 19:42 last edited by
                    #9

                    Then you may have unearthed something. Can you test that with the 5.10 branch ? Maybe the dev to see if it is still happening ?

                    Interested in AI ? www.idiap.ch
                    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                    1 Reply Last reply
                    0

                    4/9

                    25 Aug 2017, 08:06

                    • Login

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