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.


  • Qt Champions 2016

    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?


  • Qt Champions 2016

    @xtingray

    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 containers QList, QVector and the image classes Qimage, 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 from QObject? Do you delete 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?


  • Moderators

    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!


  • Qt Champions 2016

    @xtingray

    @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" in top/Task manager depends on the actual implementation. When you new 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 how malloc_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 blocks

    Now, 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)


  • Qt Champions 2016

    @xtingray

    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 the handler 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.


  • Moderators

    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 more delete'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! :)


  • Qt Champions 2016

    @xtingray

    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.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.