Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory leak in Qt5? How to get QMimeData deleted?

I just provided an answer for this question and wanted to provide a working example when I noticed that newly created QMimeData instance returned by QListModel::mimeData() won't get deleted until the application gets terminated.

So this is not a real memory leak since Qt handles all QMimeData instances on shutdown but you only have to drag&drop long enough and put the right content into your mime data to let the memory run full.

Did I miss something? Is there a way to tell Qt to delete the QMimeData instances as soon as they are not needed any more?

Please note:

I know that every instance of QMimeData gets deleted by Qt automatically on program termination. My problem here is not a real memory leak as reported by valgrind or cppcheck but it looks like the multiple and potentially very large QMimeData instances get not cleaned up at runtime which blows up memory consumption as well.

Example Code:

#include <QtWidgets>
#include <iostream>

struct TrackedMimeData : public QMimeData {
   TrackedMimeData(const QString & text) {
      std::cout << this << std::endl;
      setText(text);
   }
   ~TrackedMimeData() {
       std::cout << "~" << this << std::endl;
   }
};

struct MyListWidget : QListWidget {
   MyListWidget() {
      setDragEnabled(true);
      addItem("item1");
      addItem("item2");
   }
   QMimeData * mimeData(const QList<QListWidgetItem *>) const override {
      return new TrackedMimeData("hello");
   }
};

int main(int argsc, char *argsv[]) {
   QApplication application(argsc, argsv);
   MyListWidget gui;
   gui.show();
   return application.exec();
}

Example output looks like this:

0xa58750
0xa4e0f0
~0xa4e0f0
0xa3c6c0
~0xa3c6c0
0xa51880
0xa5ecd0
0xa31f50
0xa57db0
0xa5afc0
~0xa5afc0
0xa5aa70
~0xa5aa70
------ CLOSE WINDOW
~0xa58750
~0xa51880
~0xa5ecd0
~0xa31f50
~0xa57db0

The destructors get called before closing the application only when the drop get's accepted.

Btw. I'm on a home-grown Qt 5.6 @1fcdb6cafcf - on one computer and on 5.6.0-19.fc23 Fedora 23 precompiled on another. So I doubt it's just a temporary development state.

like image 568
frans Avatar asked Oct 19 '22 07:10

frans


2 Answers

There is a memory leak only if you forget to delete the pointer returned by mimeData(). You have to manage the ownership as with any pointer.

For instance, if you pass the mimeData() returned pointer to a QDrag object using setMimeData(), he will take ownership of it, and will take care of deleting it at the end of the drag operation. There is no memory leak in this case.

See : http://doc.qt.io/qt-5/qdrag.html#setMimeData

like image 115
galinette Avatar answered Nov 02 '22 11:11

galinette


It should be happening, if it isn't, then it's a bug - there's nothing you can do about it, other than reporting it if it hasn't been reported yet.

I can't reproduce it on OS X with Qt 5.6. It may be platform-specific, or an already fixed bug in an old Qt version. As soon as I release the mouse button at the end of the drag, the mime data gets deleted. The call stack when the destructor executes has the QDrag destructor called from the event loop via deleteLater somewhere. I've used your code verbatim.

Sidebar: It could be made just a bit more minimal, if you really wanted it as short as possible. Here's what I got, although I agree it's mostly splitting hair. Your question beats 99% other questions by providing a working example - big kudos for that! The things that I think matter for brevity are:

  1. Inclusion of the entire needed Qt module(s).
  2. Use of qDebug instead of std::cout & al, it saves an include and is more Qt style.
  3. Access specifiers don't matter if you use struct anyway.
  4. Generally speaking, destructors are either virtual in the public base class, or they can never be without succumbing to slicing. So you don't have to spell that out.

I have example code where I don't follow this, of course. I like to call it legacy code :)

#include <QtWidgets>

struct TrackedMimeData : QMimeData {
   TrackedMimeData(const QString & text) {
      qDebug() << this;
      setText(text);
   }
   ~TrackedMimeData() {
      qDebug() << "~" << this;
   }
};

struct MyListWidget : QListWidget {
   MyListWidget() {
      setDragEnabled(true);
      addItem("item1");
      addItem("item2");
   }
   QMimeData * mimeData(const QList<QListWidgetItem *>) const override {
      return new TrackedMimeData("hello");
   }
};

int main(int argsc, char *argsv[]) {
   QApplication application(argsc, argsv);
   MyListWidget gui;
   gui.show();
   return application.exec();
}
like image 20
Kuba hasn't forgotten Monica Avatar answered Nov 02 '22 12:11

Kuba hasn't forgotten Monica