Solved How to free memory from a list of images?
-
Hi,
I have been working on an application that uses a class (called PhotoHandler) which contains a QList<QImage> variable (called photoList).
When I create an instance of PhotoHandler, I fill the list with 20 QImage objects:for (int i=0; i < 20; i++) { QImage image(720, 480, QImage::Format_RGB32); ... photoList << image; }
In the destructor method of the PhotoHandler class I clear my QImage list as the first try to free memory, but obviously, it is not working at all.
Note: Tracing my program, I can confirm that the PhotoHandler destructor method is called every time its life-cycle is done inside the application.Now, using the "top" command to monitor the memory management of my program I can see how every time it creates an instance of the PhotoHandler class, then 20 additional MB are consumed by the application process. If I keep calling the related feature, I can consume 1 GB of RAM from the same application instance which starts using just 30 MB at the very beginning.
I understand this behaviour, I mean, every new class instance requires a new segment of memory so the memory consumption most be incremented, but how can I free the memory used by the previous instance of the same class that was already destroyed? What is the right procedure to release the used memory in my case?
Thanks.
-
Hi
Dont you delete your old instance? (PhotoHandler)
It should then delete the photoList and it will free the QImages? -
@mrjj Yes, I manually delete the PhotoHandler instance. In some point of my application I do:
PhotoHandler *handler = new PhotoHandler();
And then when the object life-cycle is done, I do:
delete handler;
I was expecting that this action would free some memory, but it doesn't happen. Am I doing it wrong?
-
Hello,
Your description is great but some more code will be useful.I understand this behaviour, I mean, every new class instance requires a new segment of memory so the memory consumption most be incremented, but how can I free the memory used by the previous instance of the same class that was already destroyed? What is the right procedure to release the used memory in my case?
This a very vague question to which the answer would be: deleting the object, however I have the feeling you're not after that exact answer.
On a related note, the containersQList
,QVector
and the image classesQimage
,QPixmap
use implicit sharing, so they share the memory (until a write operation is required, when they detach the data). The allocated memory itself will be freed when the last instance of the class is destroyed.Is your
PhotoHandler
class being derived fromQObject
? Do youdelete
the instances? Is it holding references to anything else beside the list of images?Kind regards.
EDIT:
When you do this:PhotoHandler *handler = new PhotoHandler();
are you storing the returned pointer?
When you do this:
delete handler;
is handler NULL or is it pointing to a memory location?
-
Hi!
If you want to find memory leaks use valgrind, not top. If you want to prevent memory leaks use smart pointers (e.g. QSharedPointer). "Why doesn't delete / free return memory to the operating system?" This question is up to the developers of your C library. The usual answer is "because it would be stupid (inefficient) to do so as long as the system still has unused physical RAM". If you really want to force the C library to give the freed memory of your process back to the system then malloc_trim(0) should do the trick.
Cheers! -
@Wieland said:
The usual answer is "because it would be stupid (inefficient) to do so as long as the system still has unused physical RAM". If you really want to force the C library to give the freed memory of your process back to the system then malloc_trim(0) should do the trick.
It's a bit more convoluted that that.
delete
does release the memory back to the heap manager (which is part of the OS) and whether or not it's viewed as "free" intop
/Task manager
depends on the actual implementation. When younew
an object, the C runtime requests that a piece of memory is allocated for your use, but it's pretty much oblivious (from the user standpoint) how the memory is managed at the OS level. Linux usually caches almost all the available physical memory and distributes it as it sees fit. This is also needed since the OS might want to do paging or swapping of some rarely used memory segments (it is the default behavior).For example on my machine the 16GB physical memory (I'm not using swap) is almost all belonging to the kernel (
top
reports a mere 300MB free). Nonetheless, that memory is still available to different processes whenever they need it. The heap manager on Windows is a bit different but in principle it has the same purpose - to manage the heap.You can think of the heap as a big jumble of used and unused memory blocks (whence the name), while the stack is a nice orderly stack of books (whence the name yet again). So the heap usually has a lot of holes (unused memory) lying around, often referred as heap fragmentation, especially if the heap manager is not very good. When you request a piece of memory for use, the manager will find you a calm cosy place to give you where you can put your variables. However, it also may decide to shuffle things around if it's needed. So for example
realloc
might actually cause a change of address (because data was moved to another place where there's enough space for the new memory block).In conclusion,
top
is not a good tool to trace memory usage (as Wieland pointed out). Although, I don't see howmalloc_trim
will do you any good in this case.Kind regards.
-
I want to thank you all for your comments and suggestions. I hope this thread could be helpful for other newbies in the future.
I had heard about Valgrind barely in the past, but I never tried it until now. I wonder that I could get some kind of warnings even about several Qt and VirtualBox libraries. I will have to read more to understand all the output this tool is giving to me. Here is the leak summary of my first test running my program for several minutes:
==7582== LEAK SUMMARY:
==7582== definitely lost: 31,305 bytes in 297 blocks
==7582== indirectly lost: 42,600,715 bytes in 10,065 blocks
==7582== possibly lost: 389,850,894 bytes in 282 blocks
==7582== still reachable: 2,242,342 bytes in 20,196 blocks
==7582== suppressed: 0 bytes in 0 blocksNow, I will try to describe my application briefly using some lines of code. My main class inherits from the QTabWidget class:
class MyMainWindow : public QTabWidget
As part of my global variables for this class, I have:
PhotoHandler *handler;
In the section of the GUI definition, I have two QPushButton objects which trigger the SLOTS where variable handler is created and deleted:
QPushButton *startButton = new QPushButton("Start session", this); connect(startButton, SIGNAL(clicked()), this, SLOT(startSession())); QPushButton *stopButton = new QPushButton("Stop session", this); connect(stopButton, SIGNAL(clicked()), this, SLOT(stopSession()));
So, in the SLOTS definition I got something like:
void MyMainWindow ::startSession() { handler = new PhotoHandler(); handler->someHandyProcedure(); ... } void MyMainWindow ::stopSession() { handler->stopSomeActions(); delete handler; }
I just would like to know if this small example follows the basics about memory management (I am aware my problem will require many Valgrind sessions and a lot of reading)
-
Hello,
I'd suggest setting the pointer to NULL after deletion and handling the case where two consecutive starts may be encountered. Another option is to add internal states to thehandler
object and use auto storage (stack variables) instead of doing everything in the heap. For example, I'd do something like:MyMainWindow::MyMainWindow(QWidget * parent) : QTabWidget(parent), handler(NULL) { } MyMainWindow::~MyMainWindow() { stopSession(); } void MyMainWindow::startSession() { stopSession(); handler = new PhotoHandler(); handler->someHandyProcedure(); // ... } void MyMainWindow::stopSession() { if (!handler) return; handler->stopSomeActions(); delete handler; handler = NULL; }
You could also consider one of the pointer-wrapper classes.
QScopedPointer
would seem appropriate in this case. Something along the lines of:class MyMainWindow : public QTabWidget { Q_OBJECT private: QScopedPointer<PhotoHandler> handler; }; void MyMainWindow::startSession() { stopSession(); handler.reset(new PhotoHandler()); handler->someHandyProcedure(); // ... } void MyMainWindow::stopSession() { if (handler.isNull()) return; handler->stopSomeActions(); handler.reset(); }
I'm a stack aficionado, so I'd actually implement it similarly to:
class MyMainWindow : public QTabWidget { Q_OBJECT private: PhotoHandler handler; }; void MyMainWindow::startSession() { if (handler.started()) return; //< Or .stop() depending on the case handler.someHandyProcedure(); // ... } void MyMainWindow::stopSession() { handler.stop(); }
and ultimately not bother about the cleanup.
I hope that helps.Kind regards.
-
Your memory management looks perfectly ok to me. But as soon as thinks become more complex (more code paths) you'll have to think harder and harder when it's necessary to delete your manually managed objects (here this is only
handler
) and you'll find yourself in the situation that you'll add more and moredelete
's. This is when smart pointers come into play. You could for example do it like this:QSharedPointer<PhotoHandler> sp_handler; void MyMainWindow::startSession() { sp_handler.reset( new PhotoHandler() ); sp_handler->someHandyProcedure(); } void MyMainWindow::stopSession() { sp_handler->stopSomeActions(); }
Now your handler object get's deleted automatically as soon as it's no longer used.
-
@kshegunov @Wieland I'm going to try the approaches you have proposed to find out which fits better for my needs. Just one last question to finish this thread:
- What means the memory information I get from the "top" command in Unix? Does it represents the "mapped" memory of the process, but not necessarily the currently "used"? When is useful that memory information or how should I interpret it?
Thanks! :)
-
Hi,
What means the memory information I get from the "top" command in Unix? Does it represents the "mapped" memory of the process, but not necessarily the currently "used"? When is useful that memory information or how should I interpret it?
It's an estimation, so you should interpret it as such: it gives you a coarse overview of memory and CPU time, but it's by no means exact. So, as Wieland suggested, it's a good idea to run your code through a profiler (as
valgrind
) and see where they might be leaks. Also adopting some of the "good practices" in programming help not having leaks in the first place. These would include but not limit themselves to:- using stack-based (actually auto-storage) allocations where possible (Incidentally the stack is also much faster).
- using wrappers for pointers (Qt provides quite a lot, you can take a look at this thread as well)
- using implicit/explicit sharing
- enforcing RAII (each class is managing and responsible for its own resources), this'd imply not sharing resources where it's posible.
Kind regards.