PDA

View Full Version : Memory Not Free immediatly after QPainter/QPainterDevice finish work



wangeen
20th July 2011, 07:50
I tested the following code, QPainter use much memory and not free even the image device is deleted, this is important for me because I need to draw polygons on thousands of images, I wish QPainter can free the memory after finish painting, is this because garbage collection or anything else?

// step 1
QImage* image[40000];


// step 2
// add about 40 M
for (int i=0; i<size; ++i){
image[i] = new QImage(15,15, QImage::Format_ARGB32_Premultiplied);
}

// step 3
// add about 660 M
for (int i=0; i<size; ++i){
QPainter painter;
painter.begin(image[i]);
painter.end();
}

// step 4
// actually not free any memory
for (int i=0; i<size; ++i){
delete image[i];
}

Santosh Reddy
20th July 2011, 08:24
what does QPainter's begin() and end() return?

wangeen
20th July 2011, 08:30
return always true, it really confuse me why eat so much memory.

Santosh Reddy
20th July 2011, 08:42
I don't see any problem with the way you are doing, may be something else is being missed in code.

wangeen
20th July 2011, 08:50
the code works fine, I mean QPainter eat too much memory, in this case about 600 Mega, and not free immediately

Added after 4 minutes:

I wish I can control the lifecycle of QPainter and free memory immediatly after it is ended.

Santosh Reddy
20th July 2011, 09:10
Life cycle of QPainter ends as soon as the block / function it is defined returns (if defined on stack).

Ok, I have done a small test, here I have just recreated what you said, it does not leak memory, as the size value increases the peak memory utilization increases (this should not be mistaken for memory leak), I have also attached the project files. Please compare the results with your software (vary the value of size as required)

6691


MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
size = 1; //40000
QTimer * timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(step1()));
connect(timer, SIGNAL(timeout()), this, SLOT(step2()));
connect(timer, SIGNAL(timeout()), this, SLOT(step3()));
timer->start(10);
}

void MainWindow::step1(void)
{
for(int i = 0; i < size; ++i)
{
image[i] = new QImage(15,15, QImage::Format_ARGB32_Premultiplied);
}
}

void MainWindow::step2(void)
{
for(int i = 0; i < size; ++i)
{
QPainter painter;
painter.begin(image[i]);
painter.end();
}
}

void MainWindow::step3(void)
{
for(int i = 0; i < size; ++i)
{
delete image[i];
}
}

ChrisW67
20th July 2011, 09:30
I tested the following code, QPainter use much memory and not free even the image device is deleted, ...
I assume you are using a blunt tool, e.g. Windows task manager, to measure the amount of memory in use by your program. Memory you have released back to the heap is often left associated with the process by the operating system unless another process is in need of it (or your process terminates). If you subsequently make heap allocations they will be granted from space already associated with your process until that space is exhausted. This is a memory fragmentation defence.

Unless a tool like valgrind is telling you that memory is actually leaking and your process grows in an unbounded way then there is nothing to see here.

Here is what valgrind does with Santosh Reddy's project on my Linux box:

...
==3679== HEAP SUMMARY:
==3679== in use at exit: 837,299 bytes in 8,035 blocks
==3679== total heap usage: 148,598 allocs, 140,563 frees, 6,774,554 bytes allocated
==3679==
==3679== LEAK SUMMARY:
==3679== definitely lost: 3,356 bytes in 12 blocks
==3679== indirectly lost: 10,160 bytes in 506 blocks
==3679== possibly lost: 325,614 bytes in 1,477 blocks
==3679== still reachable: 498,169 bytes in 6,040 blocks
==3679== suppressed: 0 bytes in 0 blocks
==3679== Rerun with --leak-check=full to see details of leaked memory
==3679==
==3679== For counts of detected and suppressed errors, rerun with: -v
==3679== Use --track-origins=yes to see where uninitialised values come from
==3679== ERROR SUMMARY: 667 errors from 102 contexts (suppressed: 6 from 1)

Very little lost.

wangeen
20th July 2011, 10:12
yeah, thank you very much for your replies, I tested your code, it's not memory leak I know, but the peak memory can reach 700 Mega, I think it's not reasonable for this case.

Added after 23 minutes:

thank you very much, I know it's not memory leak, and I tested with valgrind massif, what I can not understand is why QPainter can eat about 600 Mege memory, and what' more curious is if I deleted each image after each QPainter, the application will not reach the memory peak(700Mega).

fine code: (most memory cost less than 50Mega)
for (int i=0; i<size; ++i){
QPainter painter;
painter.begin(image[i]);
painter.end();
delete image[i];
}

not fine code: (most memory cost more than 700 Mega)
// step 3
// add about 660 M
for (int i=0; i<size; ++i){
QPainter painter;
painter.begin(image[i]);
painter.end();
}

// step 4
// actually not free any memory
for (int i=0; i<size; ++i){
delete image[i];
}

Thank you very much, I know it's not memory leak, but the peak can reach 700 Mege, it's not understandable, it is too much than we anticipate.

high_flyer
20th July 2011, 10:33
Memory deallocation does not have to happen when you deallocate.
The system may continue to hold that memory reserved for the process, even though you are freeing the memory, it is still being held for the calling process, depending on system parameters.
So you can't be guaranteed to see linear correlation between your deallocation and the memory usage you see in the system.

Second, please show your full code of your paintEvent(), it might be that you are causing this with a less than optimal code.

wangeen
20th July 2011, 10:47
actually I do nothing in the paintEvent(), just write this testcae, the issue I want to figure out is that whether Qt or system reserve the memory deleted, actually I guess it's more possiblely Qt did this, if so, is there anyway force Qt release the memory right now.

high_flyer
20th July 2011, 10:54
What is 'image'? is it a QVector?

wangeen
20th July 2011, 10:57
it's an image array.
QImage* image[40000];

high_flyer
20th July 2011, 11:58
Then its not Qt who is holding the memory after you have deleted it.

What happens if you do:


//QImage* image[40000];
QVector<QImage*> vecImages;
//allocate the images in to the vector
for (int i=0; i<vecImages.size(); ++i){
QImage *pImage = vecImages.at(i);
if(pImage)
delete pImage;
pImage = NULL;
}

vecImages.clear();

wangeen
21st July 2011, 04:50
this do work, but is there anyway to let QPainter free memory immediately after end() called?

Added after 52 minutes:

I traced the Qt source code, and got the problem, actually

when we can QPainter begin(), a engine will be created
d->paintEngine = new QRasterPaintEngine(const_cast<QImage *>(this));

and the problem is the engine is not free until the ~QImageData() is called, so it means that the memory will always there until the image is deleted. and when QPainter end() is called, the engine is only set not active, it still there, so if I want to use 4000 images, 4000 engines will be created, it's a big cost. maybe I need to rebuild the qt library to fill our requirement.



Then its not Qt who is holding the memory after you have deleted it.

What happens if you do:


//QImage* image[40000];
QVector<QImage*> vecImages;
//allocate the images in to the vector
for (int i=0; i<vecImages.size(); ++i){
QImage *pImage = vecImages.at(i);
if(pImage)
delete pImage;
pImage = NULL;
}

vecImages.clear();

high_flyer
21st July 2011, 10:17
The question is, do you need the 4000 images all being alive at the same time?
If not, just delete each image after the paint operation, based on what you said, it will free the engine associated with it as well.
If yes, then having 4000 images alive at once is going to cost you no matter what you do with the painter.
Alternatively, you can just dynamically create and destroy the painters in the paintEvent() for each image.
It essentially doesn't matter where you do it, it seems you can't come around creating and deleting some object for each paint operation (be it a QPainter, and engine, or an image)
In that case, take the one which is the easiest to implement, or, if you can measure it, the one that is the most efficient.

EDIT:
One other thing you can do:
Feed the QPainter only one image, but chage the memory in the image it self.
This will only create one Painter, with one engine.
See QImage::loadFromData().

wangeen
22nd July 2011, 03:23
got it~ thanks a lot~:)