Why is there a memory leak when I do not .Dispose() Bitmaps that I .Save() to a MemoryStream?

非 Y 不嫁゛ 提交于 2019-12-30 07:13:45

问题


Say I create a Bitmap

Bitmap bitmap = new Bitmap(320, 200);

When I write it to some stream (in my case, it's a HttpResponseStream, as given out by HttpListenerResponse), everything is fine:

bitmap.Save(stream, ImageFormat.Png);

I don't need to bitmap.Dispose(), the resources used by the bitmap will get cleaned up automatically. The problem with directly writing a Png to a non-seekable stream however is that it might result in A generic error occurred in GDI+, which happened to me when I tried my Asp app on Azure. So this is how my code looks now:

using (MemoryStream ms = new MemoryStream())
{
  bitmap.Save(ms, ImageFormat.Png);
  ms.WriteTo(stream);
}

Now unless I bitmap.Dispose() afterwards, this will leak.

Rephrased question to get more specific answers: Why does this leaking of Bitmap memory seem to depend on what type of stream I save it to?

Update: As I've been asked in comments if I'm sure it's a leak. Calling the above repeatedly in a stress test, my w3wp process will go up to gigs and gigs of memory used until my machine start swapping and it will not clean up.


回答1:


Bitmap class utilizes unmanaged resources. These resources are not related to the resources utilized by the memory stream class. You can just wrap the bitmap class in a using statement to dispose of the bitmap instance when you are finished with it.

Missed the latter part of your question. One way to "set it and forget it" is by creating a wrapper class that exposes the bitmap instance but implements a destructor that disposes of the bitmap instance. This destructor will mean the bitmap class is disposed of implicitly at garbage collection.

As a final note: Any object you instantiate that implements IDisposable MUST be disposed of by your code. Dipose will never be implicitly called. Even in your first example. Just because you save the data to a stream does not meam that memory has then been deallocated. Most of the time it is a good idea to dipose of an object within the same segment of code that instantiated it. This assists in easier to read code by boosting code transparency.




回答2:


I think the issue is the assumption that the GC will magically clean up your objects. However, it may never do so, and here's what I think may be happening:

Bitmaps use unmanaged resources to hold the bitmap data, and bitmap data is big. So you will be allocating a tiny block of managed memory and a huge block of unmanaged memory for each bitmap.

So you leave your bitmap lying around for the GC to collect at its leisure. This works well for a lot of objects because soon there is enough memory pressure that the GC collects them to re-use the memory. BUt the GC looks at the managed heap and says "By disposing the uniused objects, I can only recover 64 bytes of memory. I won't bother". It doesn't see the gigabytes of unmanaged resources, just the few bytes on its heap.

So you need to track and dispose of the bitmaps yourself.

It is possible that sometimes you have seen it clean up for you. This will be because under sme circumstances (such as when you are disposing other objects like streams with larger memory footprints, or just because it's a tuesday afternoon) it does choose to process the unused blocks of memory, and then your bitmap is disposed at last. But you cannot rely on this happening.

...Ramble:

There were two problems with pointers in the olden days.

  • They could be null, leading to crashing code
  • You could forget to free their memory/resources and get leaks

So in .net they renamed "pointer" to "reference", added the GC and pretended that the problem didn't exist any more. Except that references can still be null, and programmers do still have to track and manage their resources to avoid leaks - just a little bit less often. I think this is a bad thing - it makes us lazy and inefficient without actually eliminating the underlying problem, so then it comes back and bites us, and we end up writing reams of Dispose logic where we used to just have a simple 'delete' in our destructors.




回答3:


You must dispose the bitmap in order to release the GDI+ resources. It is that simple. It is one of the few times calling Dispose is required. If you are caching your bitmap to reduce disk access then clone the image and use the clone to save to the stream. I strongly recommend flushing, closing, and disposing of stream. When done, set clone and stream variables to null.



来源:https://stackoverflow.com/questions/8830770/why-is-there-a-memory-leak-when-i-do-not-dispose-bitmaps-that-i-save-to-a

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!