Well, this is my first foray into memory profiling a .NET app (CPU tuning I have done) and I am hitting a bit of a wall here.
I have a view in my app which loads 40 images (max) per page, each running about ~3MB. The max number of pages is 10. Seeing as I don't want to keep 400 images or 1.2GB in memory at once, I set each image to null when the page is changed.
Now, at first I thought that I must just have stale references to these images. I downloaded ANTS profiler (great tool BTW) and ran a few tests. The object lifetime graph tells me that I don't have any references to these images other than the single reference in the parent class (which is by design, also confirmed by meticulously combing through my code):
The parent class SlideViewModelBase
sticks around forever in a cache, but the MacroImage
property is set to null when the page is changed. I don't see any indication that these objects should be kept around longer than expected.
I next took a look at the large object heap and memory usage in general. After looking at three pages of images I have 691.9MB of unmanaged memory allocated and 442.3MB on the LOH. System.Byte[]
, which comes from my System.Drawing.Bitmap
to BitmapImage
conversion is taking pretty much all of the LOH space. Here is my conversion code:
public static BitmapSource ToBmpSrc( this Bitmap b )
{
var bi = new BitmapImage();
var ms = new MemoryStream();
bi.CacheOption = BitmapCacheOption.OnLoad;
b.Save( ms, ImageFormat.Bmp );
ms.Position = 0;
bi.BeginInit();
ms.Seek( 0, SeekOrigin.Begin );
bi.StreamSource = ms;
bi.EndInit();
return bi;
}
I am having a hard time finding where all of that unmanaged memory is going. I suspected the System.Drawing.Bitmap
objects at first, but ANTS doesn't show them sticking around, and I also ran a test where I made absolutely sure that all of them were disposed and it didn't make a difference. So I haven't yet figured out where all of that unmanaged memory is coming from.
My two current theories are:
- LOH fragmentation. If I navigate away from the paged view and click a couple of buttons about half of the ~1.5GB is reclaimed. Still too much, but interesting nonetheless.
- Some weird WPF binding thing. We do use databinding to display these images and I am no expert in regards to the ins and outs of how these WPF controls work.
If anyone has any theories or profiling tips I would be extremely grateful as (of course) we are on a tight deadline and I am scrambling a bit to get this final part done and working. I think I've been spoiled by tracking down memory leaks in C++ ... who woulda' thought?
If you need more info or would like me to try something else please ask. Sorry about the wall-o-text here, I tried to keep it as concise as possible.
BitmapCacheOption.OnLoad
should get rid of the stream per the docs, and I have tried manually disposing it with no luck. Thanks though, I should have added that. – Cholera