Unit tests are useful in order to guarantee code works as it is expected to work. Today my spidey sense told me I might have caused a memory leak. Let’s not consider unmanaged code for now, but wouldn’t it be great if you could write a unit test to prove that a particular object can be garbage collected? I found some interesting ideas online, but no reusable solution. What follows is my attempt at an Assert method which can be used during unit testing.
object empty = new object(); AssertHelper.IsGarbageCollected( ref empty );
It works by using a WeakReference, which references an object while still allowing that object to be reclaimed by garbage collection. The object to test is passed using the ref keyword, which allows the Assert method to set it to null. This allows it to be garbage collected.
public static void IsGarbageCollected<TObject>( ref TObject @object ) where TObject : class { Action<TObject> emptyAction = o => { }; IsGarbageCollected( ref @object, emptyAction ); } public static void IsGarbageCollected<TObject>( ref TObject @object, Action<TObject> useObject ) where TObject : class { if ( typeof( TObject ) == typeof( string ) ) { // Strings are copied by value, and don't leak anyhow. return; } int generation = GC.GetGeneration( @object ); useObject( @object ); WeakReference reference = new WeakReference( @object ); @object = null; GC.Collect( generation, GCCollectionMode.Forced ); GC.WaitForPendingFinalizers(); Assert.IsFalse( reference.IsAlive ); }
As it turns out I did write a memory leak in the code I wanted to test. 😦 At least I know about it now! As usual, I added the total code and some unit tests to my FCL library.
WARNING: Due to differences when using the Release configuration (I assume compiler optimizations), some of these unit tests will fail. Be sure to run them in Debug!
One thought on “Garbage Collection Unit Test”