-2

I am using Qt 5.7 with C++ on Visual Studio Community 2015. What I know about Qt's resource control is that, when you kill a parent, you don't need to bother destroying the pointers child to that parent object. However, when I tried, I did not get results pointing to that direction, and I can't see why.

I run below code to get a reference point. Please note the code block commented out:

#include <QtWidgets/QApplication>
#include <QWidget>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget* w0 = new QWidget;
    /*
    QWidget* w1 = new QWidget;
    w1->setWindowTitle("Window 1");
    for(size_t i = 0; i < 1000; i++) {
        QPushButton* pb = new QPushButton(w1);
    }
    w1->show();
    */
    w0->show();
    return a.exec();
}

With this code running, VS says the process memory is 4 MB.

enter image description here

And then I run below code to use more memory. Same code without the comment block:

#include <QtWidgets/QApplication>
#include <QWidget>
#include <QPushButton>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget* w0 = new QWidget;

    QWidget* w1 = new QWidget;
    w1->setWindowTitle("Window 1");
    for(size_t i = 0; i < 1000; i++) {
        QPushButton* pb = new QPushButton(w1);
    }
    w1->show();

    w0->show();
    return a.exec();
}

This time it used 9 MB of memory.

enter image description here

So far so good. Now what I would expect at this point is that, when I destroy w1, it should return the resources used by its child objects (push buttons) and I should see a decrease in the memory used. But it does not happen. I kill w1 and w0 is still running so I can still observe the memory usage, w0 is not a parent of w1, all push buttons are child to w1, yet the memory isn't returned. What am I doing/understanding wrong?

Update: In the above examples I just close (click the X up there) the window pointed to by w1, I think it would delete the pointer w1 too but to test it I have run the below code, and that code uses 6 MB of memory. So apparently 3 MB is returned after adding

delete w1;

New code below:

#include <QtWidgets/QApplication>
#include <QWidget>
#include <QPushButton>

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    QWidget* w0 = new QWidget;

    QWidget* w1 = new QWidget;
    w1->setWindowTitle("Window 1");
    for(size_t i = 0; i < 1000; i++) {
        QPushButton* pb = new QPushButton(w1);
    }
    w1->show();

    w0->show();
    delete w1;

    return a.exec();
}

but there is still a 2 MB leak. Two questions at this point:

1. Why do I have to explicitly delete w1? Why is closing the window not enough? 2. Why is there still a leak even after I explicitly delete w1?

Deniz
  • 509
  • 7
  • 26
  • How exactly are you *killing* `w0` and `w1`? You aren't showing that in your code. – Mike Dec 16 '16 at 17:18
  • In the example above I just close the window w1 is pointing to, thinking that it would also destroy the pointer (does it?). Now I have just run a test and added `delete w1;` below `w0->show();` and now the memory used is 6 MB, which is still not 4 MB. So apparently yes there is a difference when I delete the pointer myself but it still seems to be leaking, and I think that closing the window should be enough to return the memory used. – Deniz Dec 16 '16 at 17:23
  • 1
    No, closing the window does not delete unless you [set the `WA_DeleteOnClose` attribute](https://doc.qt.io/qt-5/qwidget.html#close). Have you set that? – Mike Dec 16 '16 at 17:30
  • @Mike No I have not, but I have made a new test with a `delete w1;` which I think should be enough to get the 5 MB used by w1. But I still get a 2 MB leak even after that delete. – Deniz Dec 16 '16 at 17:32
  • 1
    Side note: you could simply `#include ` with no other includes needed. Also, the form `#include ` is wrong, as it hides incorrect project configuration until link time. Use `#include `. If the compiler complains of a missing include file, then you know that you must check the `.pro` file for correct modules, and then re-run qmake on the project. – Kuba hasn't forgotten Monica Dec 19 '16 at 17:45
  • @KubaOber Thank you for that information. Those includes are actually coming from Visual Studio's Qt add-in. But I would have included them myself the same way I think, so thank you. – Deniz Dec 22 '16 at 06:22
  • 1
    Ultimately, the responsibility for your code is yours. The plugin may give you hints - it may even give you wrong hints. You must understand any code a 3rd party gives you before you use it blindly. Including Qt plugin. – Kuba hasn't forgotten Monica Dec 22 '16 at 15:59
  • @KubaOber you are right. thank you again for sharing this information with me. – Deniz Dec 22 '16 at 18:30

2 Answers2

4

It is not necessary that your operating system memory manager will release each and every deallocated heap memory byte. Usually it does not. But that doesn't mean that the memory has leaked.

For example, you may begin with 5 mb of memory usage, then allocate another 5 mb worth of objects, and when deallocated it only frees 3 mb and you are left with 7 mb usage. But if you allocate the 5 mb worth of objects again, memory usage will not jump to 12 mb, it will still jump to 10, because those 2 unreleased mb will be reused.

Take a look here, where I investigated one such issue. As you can see, for a while the memory usage keeps growing, from 41 to 53 mb, but then it stabilizes and it doesn't grow further even after hundreds of allocations/deallocations. Different OS memory managers work in a different way, the amount total memory and free memory are also a factor, possibly the application's own usage patterns as well.

It can be regarded as a leak only if each time you do that the memory usage increases by 2 mb and never reaches a stable level. If you make a custom widget with a debug message in the destructor, you can verify when and whether it and its children are being destroyed. But again, there is no guarantee that you reclaim 100% of the used memory.

Community
  • 1
  • 1
dtech
  • 47,916
  • 17
  • 112
  • 190
  • Thats a very good point, I was going to mention it in my answer, but I am not sure that VS2015 shows in memory monitor.. it can show memory usage from OS point of view and then you completely right or in theory it can shows proper allocations via c++ runtime, then again in theory it shouldn't be affected by memory allocation from OS.. But as I am on 2013 still I can't test it.. But I would vote for your answer =) – evilruff Dec 16 '16 at 17:39
  • @evilruff's and Mike's comments were very helpful, letting me know about the flag to destroy the object upon close. However, what really cleared things up for me was your answer so I am setting it as the accepted answer. I have upvoted Mike's and evilruff's answers for they also helped me. Thank you all three. – Deniz Dec 16 '16 at 17:42
1

Assumption you make is wrong, then you close a window it's just hiding by close signal. There is no destructor call as long as you don't set a flag Qt::WA_DeleteOnClose, then you widget will be removed on close.

dtech
  • 47,916
  • 17
  • 112
  • 190
evilruff
  • 3,947
  • 1
  • 15
  • 27
  • Thank you for this information. However, I have updated my question with a new test including an explicit `delete w1;` and it returns 3 MB back but there is still a 2 MB leak. How should I interpret this situation? – Deniz Dec 16 '16 at 17:31
  • 1
    Well, there are many internal initialization happens inside Qt then you using different component, like QPushButton for example. It creates different sort of cached things, for example if you going to create more QPushButtons after that.. To be honest I wouldn't bother.. if you put a loop over your code, say with 10000 iterations you will see that nothing wrong really happens – evilruff Dec 16 '16 at 17:34
  • Also many things are deleted using deleteLater() so it will only happens after event loop – evilruff Dec 16 '16 at 17:37