Teaching an old dog new tricks: GC fun in Whidbey

The GC does an amazing job of
managing managed memory… We really work hard to make sure pages stay hot, and
the memory is cached where you need it…. But the reality of the world is that
for many folks it is not just managed memory they have to deal with. Often you have to deal with memory
allocated by legacy APIs in an unmanaged heap which the GC does not directly
manage or external non-memory resources. We added a couple of neat features to the
CLR in Whidbey to help address these issues. I was just in the middle of adding
slides about these to my Design .NET Class Libraries class that I teach to WinFX
(and other) developers here at Microsoft and I thought I’d share about these
cool features…"urn:schemas-microsoft-com:office:office" />

  1. Cheap managed object holding an
    expensive unmanaged object.

Consider a class that has a very
small managed instance size but holds a pointer to a very large chunk of
unmanaged memory. Even after no one
is referencing the managed instance it could stay alive for a while because the
GC sees only the managed instance size it does not think it is “worth it” to
free the instance. So we need to
“teach” the GC about the true cost of this instance so that it will accurately
know when to kick of a collection to free up more memory in the process.


Useful
when you have a disproportionate ratio of managed, to unmanaged
resources


GC alters
it’s strategy, to increase the number of collections
performed


GC.RemoveMemoryPressure when your
object is freed, to allow the GC to return to its standard
strategy

class Bitmap
{

   private long
_size;

   Bitmap (string path )
{

   _size = new
FileInfo(path).Length;

  
GC.AddMemoryPressure(_size);

   // other
work

   }

   ~Bitmap() {

  
GC.RemoveMemoryPressure(_size);

// other work

   }

}

In the constructor we “add pressure”
to the GC in the form of the number of bytes the bitmap will store in unmanaged
memory and in the finalizer we remove that pressure.

  1. Handling a limited number of
    resources.

In some cases there exists only a
limited number of a resource (a HDC, Hwnd, database connection, etc) and you
want to manage your usage of those resources carefully, but you’d like to hide
that management from your users. The HandleCollector is here to help! It
essentially does the bean counting for you and kicks the GC when you start to
run out of resources to perform a collection to try to free some of the
resource. Notice this basic
strategy was used privately by WinForms in V1 and is now made public in
Whidbey.

 


HandleCollector keeps track of a
limited number of handles


typically, unmanaged resource
handles: HDCs, HWnds, etc


When you
allocate a new handle, call Add.


When you
freeing, call Remove


As you
add to the collector, it may perform a GC.Collect, to free existing handles,
based on the current count, and the number of resources
available

HandleCollector(string name,
int initialThreshold, int
maximumThreshold);

name: allows you to track each handle
type separately, if needed

initialThreshold: the point at which collections
should begin being performed

maximumThreshold: the point at which collections
MUST be performed. This should be set to the maximum number of available
handles

static readonly
HandleCollector GdiHandleType =

new HandleCollector( “GdiHandles”, 10, 50);

static IntPtr
CreateSolidBrush() {

   IntPtr temp =
CreateSolidBrushImpl(…);

  
GdiHandleType.Add();

   return temp;

}

internal static void
DeleteObject(IntPtr handle) {

  
DeleteObjectImpl(handle);

  
GdiHandleType.Remove();

}