PDA

View Full Version : [Memory managament] long liveliness of application



Blood9999
9th August 2012, 20:40
I write big application which will have to run 24/7, without turning it on and off. It has many dialogs, popups and stuff (it is application to manage production). My problem is that i have noticed after dialog pops up and someone close it, memory doesn't return to state before event occured. I mean, if application used 14000 KB of memory before dialog, it pops up, close and application uses 14100KB (even if dialog doesn't store any data). I read that all widgets used in my widget have to have parent set, so when my widget is deleting, all child widgets will be also deleted, but i have done this condition. How can i be sure that i release all memory?

alitoh
9th August 2012, 21:09
It's hard to blind guess such a leak, mainly because it's usually something one tends to overlook because of it's level of obviousness. Off the top of my hat, when I started I used to not call the QWidget constructor in my custom QWidget constructor.

Also, are you sure you are not just instanciating a new widget every time you open it?

if you do something like this:


QPushButton * qpbOfFunAndJoyAndHappyness = new QPushButton("Push Button");
qpbOfFunAndJoyAndHappyness->show();


every time you want to show a button (just to give an example, I am not saying this is your case), this will always generate a new instance of it, which when you close you lose direct control of and which will only be deleted when you delete the instance of whatever it is that's calling those two lines of code. (and even then, only if parenting is done properly)

Again, this is just from my experience. Common errors I used to make all the time (and that even now I do every now and then, specially when I am too obsesed with deploying a prototype and not minding memory performance).

My best advice is to check step by step what's happening with the widgets. Try to pinpoint which one is the one that's leaking and then track it.

Sorry I couldn't be of much help, but it's really hard without something to work on.

Blood9999
9th August 2012, 21:19
Yea, of course, im stupid. I forgot to show you how i'm doing it. This is just a template of how i invoke my dialogs:


/*somewhere in main window constructor*/
button = new QPushButton("Show dialog", this);

connect(button, SIGNAL(clicked()), this, SLOT(showDialog()));

/*...*/

void myMainWindow::showDialog()
{
dialog dial(this);

dial.setGeometry(100,100,200,200);
dial.exec();
}

dialog.cpp:


dialog::dialog(QWidget *parent)
: QDialog(parent)
{
label = new QLabel("Seomthing", this);

edit = new QLineEdit(this);

button = new QPushButton("My Button", this);

lay = new QVBoxLayout(this);
lay->addWidget(label);
lay->addWidget(edit);
lay->addWidget(button);

setLayout(lay);
}

That's how it looks like but much, much simpler than it is in real project. And what i think is that dial will be created in showDialog() function, but when we exit from this function, dial object should be deleted (which outcome from C++ variables locality) along with all its child widgets. Am i right?

alitoh
9th August 2012, 21:43
Isn't .exec() a slot? why not just instance your dialog widget in your main window constructor and connect your click() signal to the slot exec()?

Also, if your code happens to work as it is right now, it wouldn't surprise me that it might be leaking. Think about it; you assume that when going out of scope of the showDialog() method it will delete local stuff, which is right, but then you exec something that, from what I understood, does not close the exact same moment the showDialog() method finished, hence it did not get deleted (or it would either close the dialog or crash).

Another option that I think would work for your code as it is right now, is to set the attribute Qt::WA_DeleteOnClose (http://qt-project.org/doc/qt-4.8/qwidget.html#setAttribute) to your dial instance.



void myMainWindow::showDialog()
{
dialog dial(this);
dial->setAttribute(Qt::WA_DeleteOnClose);
dial.setGeometry(100,100,200,200);
dial.exec();
}


if that works, then what I said earlier is true. Your assumption about the object being deleted is wrong (can't tell why, though) and that is clear because the dialog itself is not insta-closing.

Blood9999
9th August 2012, 22:01
I have to do it this way, because after dial.exec() i'm doing some more stuff, depends on if dialog was accepted or rejected.


from what I understood, does not close the exact same moment the showDialog() method finished, hence it did not get deleted (or it would either close the dialog or crash)

Actually i did some experimets. I wrote my own QLabel, i mean class which inherits from QLabel and which shows up message box when destructor is called.


class myLabel : public QLabel
{
Q_OBJECT
public:
myLabel(const QString & text, QWidget * parent = 0) : QLabel(text,parent) {};
~myLabel() { QMessageBox::information(this, "asd", "label destructor"); };

};

And in dialog.cpp


dialog::dialog(QWidget *parent)
: QDialog(parent)
{
label = new myLabel("Seomthing", this);

edit = new QLineEdit(this);

button = new QPushButton("My Button", this);

lay = new QVBoxLayout;
lay->addWidget(label);
lay->addWidget(edit);
lay->addWidget(button);

setLayout(lay);
}

dialog::~dialog()
{
QMessageBox::information(this, "asd", "Dialog destructor");
}

mainwindow.cpp


void test::showDialog()
{
dialog dial;

dial.setGeometry(100,100,200,200);
dial.exec();

QMessageBox::information(this, "asd", "Function");
}

And what i'm getting in result of this program (after showing dialog) is:

Function
Dialog destructor
label destructor

So it means that destructors are called fine, but still in this simple example. Memory usage before dialog: 2160K, during dialog is opened: 2668K, after dialog closed: 2408K (i did measurs without messageboxes)

EDIT

If i add this flag, program is crashing after window closes.

alitoh
9th August 2012, 22:05
Have you tried the deleteOnClose flag?

Blood9999
9th August 2012, 22:29
Yes i did.

amleto
10th August 2012, 00:35
Think about it; you assume that when going out of scope of the showDialog() method it will delete local stuff, which is right, but then you exec something that, from what I understood, does not close the exact same moment the showDialog() method finished, hence it did not get deleted (or it would either close the dialog or crash).


sorry, but 'exec'ing something' does not break the rules of c++ which is that all stack instances are destructed on scope exit. So if dialogs are on the stack, they will dtor when they leave scope. In this case, WA_DeleteonClose is redundant - it is only needed for widgets on the heap.


it should be pointed out that the op is judging a memory leak from o/s memory tools which is definitely not a suitable tool for such a job and can often be misleading.



So it means that destructors are called fine, but still in this simple example. Memory usage before dialog: 2160K, during dialog is opened: 2668K, after dialog closed: 2408K (i did measurs without messageboxes)
This is worthless information as I explain above.

WA_DeleteOnClose flag appears to cause 'double deletion' for widgets on the stack => crash.

alitoh
10th August 2012, 01:20
sorry, but 'exec'ing something' does not break the rules of c++ which is that all stack instances are destructed on scope exit. So if dialogs are on the stack, they will dtor when they leave scope. In this case, WA_DeleteonClose is redundant - it is only needed for widgets on the heap.

One second thought, should he be calling the QWidget destructor as he did with the constructor? His redefinition might not be calling it, only printing some misleading debug messages.

I am surprised at how something can be deleted from either the heap or the stack, and still apparently work. I just tried doing that with a QWidget in a simple qt4 app and it just "closes" itself (destructs) on scope exit.

ChrisW67
10th August 2012, 02:57
One second thought, should he be calling the QWidget destructor as he did with the constructor? His redefinition might not be calling it, only printing some misleading debug messages.
Destructors are invoked automatically when the object lifetime ends (i.e. out-of-scope for stack objects, delete for heap objects), you don't call them directly. (Actually exceptions thrown in certain ways can cause destructors to be skipped but your program is terminating horribly anyway)


I am surprised at how something can be deleted from either the heap or the stack, and still apparently work. I just tried doing that with a QWidget in a simple qt4 app and it just "closes" itself (destructs) on scope exit.
I have no idea what you mean by "still apparently work." If an object is destroyed it cannot work. When a stack-based object goes out of scope it is destroyed: no ifs, no buts, just destroyed. An object on the heap persists and continues to exist and work until something in the code causes delete to be called on it; then it is destroyed. This is plain C++.

alitoh
10th August 2012, 04:51
I have no idea what you mean by "still apparently work." If an object is destroyed it cannot work. When a stack-based object goes out of scope it is destroyed: no ifs, no buts, just destroyed. An object on the heap persists and continues to exist and work until something in the code causes delete to be called on it; then it is destroyed. This is plain C++.
Read the OP. He is not using heap memory, he is using stack-based objects *and* at the same time they don't seem to be getting terminated when going out of scope. That's been exactly my point this whole time. Also, misleading or not, his is an application that runs 24/7, and he clearly stated that he has noticed a constant increase in memory consumption. That's completely aside from whichever method for measuring of memory consumption you are using. It's a visible fact and should be addressed. Is it the best diagnosis? No. Is it a wrong diagnosis? No. If the same process keeps making the application's memory consumption to constantly escalate, there is clearly a leak. That is plain C/C++ style memory management.

I am not questioning the C++ behaviour, poindexter, I am saying that as far as I understood his problem, his dialog window is persisting, regardless of the scope the application is currently in, which in turn seems to suggest something is not getting deleted. Otherwise his problem would be "my dialog appears and disappears" or "dialog closes up", which are entirely different issues than what's being currently described as the problem. I am not suggesting this is the way C++ or Qt works. There's a big difference.

He executes showDialog(), which in turn instantiates and executes a dialog, this thread moves on, OUT of the showDialog() scope into whatever else there is in the callstack, but the dialog is still showing/working. This is NOT expected behaviour for the currently provided code because, as you said, it is not C++ behaviour.

The way he wrote his application, when showDialog() instantiates the dialog and executes it, it "appears" on-screen, but as soon as that method is over, the dialog should close itself because of scope variables deletion. This doesn't seem to be the case. What seems to be the current behaviour is what I mentioned before.

Blood9999: Can you define what's currently happening? Is your dialog closing when going out of the showDialog()'s scope? Or does it persist?

Blood9999
10th August 2012, 12:11
From user point of view everything works perfectly fine. After click on button, new dialog appers. When i close it, function ends and destroys dial object ( i checked that via messageboxes in destructors) so memory should get back to earlier 'state', but it doesn't. Weird stuff...

amleto
10th August 2012, 12:38
He executes showDialog(), which in turn instantiates and executes a dialog, this thread moves on, OUT of the showDialog() scope into whatever else there is in the callstack, but the dialog is still showing/working. This is NOT expected behaviour for the currently provided code because, as you said, it is not C++ behaviour.

The way he wrote his application, when showDialog() instantiates the dialog and executes it, it "appears" on-screen, but as soon as that method is over, the dialog should close itself because of scope variables deletion. This doesn't seem to be the case. What seems to be the current behaviour is what I mentioned before.


wrong and wrong.

The examples shows `dialog.exec();`. This is a blocking call that spins up another event loop. When the event loop exits (ie when the dialog closes), the next line will be executed and eventually control will leave that method's scope and the dialog will be destructed!

blood, if you truly have a leak, it doesn't come from this scenario. Get a proper memory profiler, even if only a trial version.

yeye_olive
10th August 2012, 12:47
He executes showDialog(), which in turn instantiates and executes a dialog, this thread moves on, OUT of the showDialog() scope into whatever else there is in the callstack, but the dialog is still showing/working.

The way he wrote his application, when showDialog() instantiates the dialog and executes it, it "appears" on-screen, but as soon as that method is over, the dialog should close itself because of scope variables deletion. This doesn't seem to be the case. What seems to be the current behaviour is what I mentioned before.
I am sorry but I cannot see how you can infer that from any of the OP's posts. Quite the contrary actually: I understood that the dialogs disappeared as expected. The OP's problem was the constantly increasing memory usage, which, as already explained above, cannot reliably be related to a memory leak.

@Blood9999
There might be a memory leak in another part of your program. Have you tried to run a tool such as valgrind?

EDIT:
@amleto: sorry, I hadn't refreshed to see your last post.

Blood9999
10th August 2012, 14:14
@yeye_olive
I write it on Windows, because it has to work on this system, so i suppose i would need to use another tool. I will have to use something because it is driving me crazy. Also i write it in Visual Studio 2010 Ultimate, so maybe is there any tool to do that?

amleto
10th August 2012, 14:17
I think this is what I used at a former job http://www.softwareverify.com/cpp-memory.php free 30 day trial iirc


I've just come across gperftools as well (google performance)
http://code.google.com/p/gperftools/?redir=1

d_stranz
11th August 2012, 00:36
As amleto and others have said, using Task Manager as a way to monitor subtle memory use is misleading. You can use it to watch gross memory allocation - going from 200 to 600 MB when some huge file gets read into internal data structure, for example, but for looking at the memory used and freed by a single dialog box, it isn't going to give anything close to an accurate answer.

Besides, I don't even think that Task Manager is looking at stack-based memory; I think it is probably watching the heap, which your stack-based dialog isn't using.

So, look elsewhere. What is your dialog doing? Is something inside it allocating heap memory that doesn't get released? Is something that happens after the dialog closes doing it? Is it a one time bump up in memory, or does memory continue to grow every time the dialog gets executed? What happens to memory if you just let the program sit there and run and use other parts of it, but not the dialog?

Also remember that heap memory gets fragmented as more and more objects are allocated and then freed. Each time you ask for something from the heap, the memory manager has to go find a contiguous chunk big enough to hold it. Depending on how often the memory manager consolidates memory that has been freed, it could find this block in memory that has already been used and discarded or it may have to go out and get some more. If you do a lot of allocation and freeing of objects, your memory footprint may continue to grow even though the sum of all the memory used by the active objects remains relatively constant. This doesn't mean a leak, it just means the memory manager can't keep up with the demand.

alitoh
15th August 2012, 14:23
What is your dialog doing? Is something inside it allocating heap memory that doesn't get released? Is something that happens after the dialog closes doing it? Is it a one time bump up in memory, or does memory continue to grow every time the dialog gets executed? What happens to memory if you just let the program sit there and run and use other parts of it, but not the dialog?

This. This is the best advice so far to your problem (assuming you pinpointed it to that particular code fragment).


The examples shows `dialog.exec();`. This is a blocking call that spins up another event loop.

Are you 100% sure? I could swear I read somewhere that it starts a new thread, but you seem pretty sure about it. If this is the case, I have a serious misconception of it, and I can't find anything on the documentation to confirm or deny it. Please give me a pointer.

tescrin
15th August 2012, 16:18
If he's using Task Manager, let's say; and he get heavy fragmentation from the awkward placement of objects (again, an assumption) then it's possible that this fragmentation causes task manager to "see" an increasing memory usage because over time larger objects (such as dialogs) have to keep allocating new blocks of space that are big enough; despite all of the memory being released.

Imagine a scenario (assuming I follow amleto correctly) where using the .exec command delays the destructor:



myclass::stuff()
{
QDialog someOtherDialog(this);

if(true){
QDialog someDialog(this);
somedialog.exec()
someOtherDialog.exec() //allocated while previous dialog hasn't destructed yet! It's only "rejected" or "accepted"
}
//someOtherDialog is still in scope while someDialog has now destructed
}

Assuming he's used only contiguous memory to this point the second dialog piles on top of the old one. Assuming they had different scopes as in the above (I may be incorrect; I just thought that I remembered if and loop statements had their own scope) then the next time something is allocated; especially if it's before either dialog has gone out of scope it will allocate new memory without actually being a leak.

On a side note, use valgrind!

d_stranz
17th August 2012, 18:36
Assuming they had different scopes as in the above

The if clause establishes a different scope (as does the myclass::stuff() method); "someDialog" is allocated when the if clause is entered and deleted when it exits (on the *stack*, not the heap). "someOtherDialog" is allocated when stuff() is entered, remains in scope through the if clause, and is deleted when stuff() exits (also on the *stack* not the heap). This is basic C++, it has nothing to do with Qt and the fact that what is being allocated is QObject-based. These could be any old C++ class or data object; so long as they are allocated on the stack, when they go out of scope, they're gone.

As far as I know, this does not cause fragmentation, although the stack may have to grow to accommodate the two dialogs. Once each scope is exited, the stack pointer is set back to wherever it was prior to the new scope. The stack is always contiguous memory, as far as I know; the stack pointer just gets moved around.

maximebd
17th August 2012, 21:06
The C++ stack is pre-allocated and its size never changes. Whatever you allocate on the stack you will never see in the task manager or in a memory profiler that hooks on memory allocation functions.
Qt uses PIMPL (http://en.wikipedia.org/wiki/Private_class_data_pattern) a lot, so any QObject allocated on that stack will actually have most of its data allocated on the heap.
Variations of memory of +/- 100K is not a memory leak. They are often symptoms of an undisclosed implementation detail or OS memory management.
I wouldn't worry about memory fragmentation until your application gets to 2GB of memory usage.


Since the OP never claimed leaks of more than 100K over the lifetime of the application, I really wouldn't a memory leak there a all. Unless you have clear evidence that over a period of time, memory usage by the application has increased by at the very minimum 10MB (100MB would make it better), then I would just attribute that +/- 100KB to the task manager being not precise.

d_stranz
17th August 2012, 21:28
Thanks for the clarification about the stack. I had forgotten about the pre-allocation; I guess that's why you get stack overflow sometimes :-) Likewise with PIMPL; if a QDialog contains other QWidgets, all of those will certainly be heap-allocated even if the dialog object itself isn't.

And on Windows on my box at least (with 2GB RAM total), I can't get much over 1 GB of heap allocation before I run out of memory for a single app.

amleto
17th August 2012, 23:22
windows has single process memory limits as well