The way WPF manages memory is a bit of a mystery. It all appears to be nicely garbage collected and everything, but I know there are unmanaged resources under there. How does it know when to garbage collect them?
This is especially important for my application, since I have to manage MANY photos on the screen at once. I’m pretty comfortable with the Dispose() idiom – I kind of like knowing where the memory is going when I’m using such large chunks. But with WPF it’s been a mystery.
Enter: .Net Reflector! Not that I would use such a thing. But supposing I did. (Incidentally, I think I owe Lutz Roeder several beers because of this program)
For those that don’t know, there’s a native code library called ‘milcore’ that WPF is built upon. It has a number of handle types. These are wrapped in .NET by subclasses of SafeMILHandle. This is where things become clear – each of these handle wrappers manually puts pressure on the garbage collector according to the estimated size of the object in question. It uses something called MemoryPresssure.Add() to do this.
Which, it turns out, is not actually a way to influence the garbage collector. It’s in PresentationCore.dll: MS.Internal.MemoryPressure. It uses a funny time/size based heuristic to manually call GC.Collect, which we all know and love. So, depending on what you’re doing with MIL Handles and how you’re doing it, WPF will force garbage collection when it feels like it.
Now, I don’t know a whole lot about garbage collectors, but… how can this possibly work? It seems fine in simple cases (allocate a whole ton of memory, null out your references, repeat). But doesn’t it break the useful heuristics that makes gc perform well? For the case of bitmaps especially, it seems that it’s important for GC to know that these are large objects that should be released as soon as possible. More bang for the buck there. (The large object heap comes to mind, but I think that’s to prevent heap fragmentation)
It’s interesting to observe that the base SafeHandle class *does* implement IDisposable. But the MIL handles themselves aren’t publicly accessible, so they can’t be directly used. But an adventurous/insane person might use reflection to pull the MIL Handle out, replace it with null at the source, type it as an IDisposable, and dispose it manually. I may resort to such measures if this memory pressure thing turns out to be as fishy as it smells.