I'm writing a control to show images.
My problem comes out using Image class on multipage TIFFs. I use this (I post only relevant code) at the beginning:Image img;
int pages;
img = Bitmap.FromFile(filename);
pages = img.GetFrameCount(FrameDimension.Page);
then, when the user wants to show a different page:
public override Image GetPage(int page)
{
if (page < 1 || page > pages) return null;
try
{
#if !TEST
img.SelectActiveFrame(FrameDimension.Page, page - 1);
return new Bitmap(img);
#else
MemoryStream ms = new MemoryStream();
img.SelectActiveFrame(FrameDimension.Page, page - 1);
img.Save(ms, ImageFormat.Jpeg);
Image ret = Image.FromStream(ms);
ms.Flush();
ms.Dispose();
return ret;
#endif
}
catch (Exception ex)
{
"Tiff GetPage error: {0}".ToDebug(ex.Message);
return null;
}
}
Using img.SelectActiveFrame(FrameDimension.Page, page - 1);
(in both versions) about 7MB are allocated in memory and those are never freed (even exiting the method)!!!
SelectActiveFrame()
) + z (Image ret = ...
). Well, I should have x + z (y part should be zero or GC collected exiting the method), but obviously that's not what happens, even calling GC.Collect
manually.
Going back to a previously visited page, memory increases effectively only with z, as expected.
I find it terrible (think about a file with 80 pages...), but how can I force img object to free allocated memory? Am I doing something wrong?
I've already thought closing and reopening img, but speed is not good.
Thanks to everybodyDon't use new Bitmap(img) because it will force the system to create new memory for a new Bitmap object using 32-bit color by default.
You can just use var bitmap = (Bitmap)img; to retrieve a Bitmap object for that page.
If I'm not mistaken you're not destroying the used controls at every possible point. I think you might need to test this - but make sure that you're disposing all the used controls.
ie. ImageControlUsed.Dispose();
I answer my question after trying different solutions and accept soluzion given by user965487 because at the end he was right (thanks to Hans Passant too).
If you have a class (call it QV) similar to this
public class QV
{
Image img;
int pages;
public QV(filename) {
img = Bitmap.FromFile(filename);
pages = img.GetFrameCount(FrameDimension.Page);
}
~QV() {
img.Dispose();
img = null;
}
public Image GetPage(int page) {
if (page < 1 || page > pages) return null;
img.SelectActiveFrame(FrameDimension.Page, page - 1);
return new Bitmap(img);
}
}
then every time you call GetPage(...)
your memory will grow not only for the size of returned image, but also for img.SelectActiveFrame(...)
statement. I don't know why and how, but it happens. Releasing returned image and calling frees memory for image size, not for the amount taken from SelectActiveFrame() (anyway this memory is not duplicated if you return on a previously seend page).
So you'd better open and close the image everytime, like this:
public class QV
{
Image img;
int pages;
public QV(filename) {
img = Bitmap.FromFile(filename);
pages = img.GetFrameCount(FrameDimension.Page);
img.Dispose();
}
public Image GetPage(int page) {
if (page < 1 || page > pages) return null;
img = Bitmap.FromFile(filename);
img.SelectActiveFrame(FrameDimension.Page, page - 1);
Image ret = Bitmap(img);
img.Dispose();
return ret;
}
}
Payload for opening and disposing image everytime user requests a new page is really nothing compared to dangerous memory allcation done with first solution.
I hope someone needs this.
精彩评论