Geeks With Blogs

New Things I Learned

In my post yesterday, I created a rudimentary mechanism to detect if/when an object is garbage collected in .NET.  In the example I gave, I created a ListCollectionViewEx class deriving from ListCollectionView.  The sample code then tries to create 100 ListCollectionViewEx and then for a GC.Collect.  To my surprise, the ListCollectionViewEx objects are not garbage collected.

Now, I know the mechanism to record garbage collected item works, because I've tried it with other objects.  So what gives?  From MSDN, it states that ListCollectionView is 'Represents the collection view for collections that implement IList.'.  CollectionView is its base class, which should be used for collections that are not in List format.  You can see that both does not have a parameterless constructor, CollectionView requires an IEnumerable to be passed, and ListCollectionView requires an IList to be passed - it then uses that collection to provide a view for that collection.

One of the nice advantages to using these is that if the underlying collection changes, and the collection implements INotifyCollectionChanged interface, the view also will show those changes.  Based on this knowledge, we can surmise that those classes will be a subscriber to the CollectionChanged event.  This causes a relation (object graph) to be created from the underlying collection.  I'll illustrate it in the code snippet below:

public class Test
{
   private INotifyCollectionChanged _list = new ObservableCollection<string>();
 
   public INotifyCollectionChanged List
   {
      get { return _list; }
   }
 
   public void Subscribe()
   {
      _list.CollectionChanged += _list_CollectionChanged;
   }
 
   void _list_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
   {
      // Do stuff
   }
}

In the above code, I have a Test class that contains a list (which is an ObservableCollection - implements INotifyCollectionChanged) - whenever a Test object is created, the list will also be created and since it is contained in the Test class, you can draw an object graph from that Test object to the list object.  Because of this, the list object can't be garbage collected until the Test object is not reachable (contained or referenced).

The function will then have the Test object subscribe to the CollectionChanged event from the list - since the event originates from the list to the subscriber (Test object), this creates another object graph from the list to the Test object, which meant the list now references the Test object.  This also means that while the list is referenced by another object, the Test object and the list object can't be garbage collected.

We're assuming that CollectionView/ListCollectionView works similarly where it subscribes to the CollectionChanged event of the collection being passed in the constructor.  Is there a way to verify this?  It's a good thing that we're using our own collection class, the ThreadAwareObservableCollection<T>.  So I created a helper function in it for verification.

public class ThreadAwareObservableCollection<T> : ObservableCollection<T>
{
   public string[] GetCreationIdsFromSubscribers()
   {
      System.Collections.Specialized.NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
      if (eventHandler == null)
         return new string[0];
 
      List<string> creationIds = new List<string>();
      Delegate[] delegates = eventHandler.GetInvocationList();
      // Walk thru invocation list
      foreach (System.Collections.Specialized.NotifyCollectionChangedEventHandler handler in delegates)
      {
         ListCollectionViewEx lcv = handler.Target as ListCollectionViewEx;
         if (lcv != null)
            creationIds.Add(lcv.CreationId);
      }
 
      return creationIds.ToArray();
   }
   public void RemoveAllCollectionViewSubscriber()
   {
      System.Collections.Specialized.NotifyCollectionChangedEventHandler eventHandler = CollectionChanged;
      if (eventHandler == null)
         return;
 
      Delegate[] delegates = eventHandler.GetInvocationList();
      // Walk thru invocation list
      foreach (System.Collections.Specialized.NotifyCollectionChangedEventHandler handler in delegates)
      {
         if (handler.Target is ListCollectionViewEx)
            CollectionChanged -= handler;
      }
   }
}
 
private void TestFunction()
{
   for (int i = 0; i < 100; i++)
   {
      ListCollectionViewEx lcv = new ListCollectionViewEx(ItemCollector.ItemsCreated);
   }
 
   GC.Collect();
 
   string[] creationIds = ItemCollector.ItemsCreated.GetCreationIdsFromSubscribers();
   ItemCollector.ItemsCreated.RemoveAllCollectionViewSubscriber()
   GC.Collect();
   creationIds = ItemCollector.ItemsCreated.GetCreationIdsFromSubscribers();
}

I've added helper functions in the ThreadAwareObservableCollection<T> class that helps in getting the creation Ids of the ListCollectionViewEx subscribers, and another one to remove all ListCollectionViewEx subscribers from its CollectionChanged event.  Running the code above and stepping through the TestFunction shows that after a GC.Collect(), the number of subscribers doesnt' change even after the GC.Collect, and the creationIds returned are the creation Ids from the ListCollectionViewEx class.  Calling the method to remove CollectionView subscribers, then calling GC.Collect a second time will populate our collection that contains destroyed items.

This shows a fairly dangerous repercussions; collections that gets viewed via CollectionView (or its derived classes) will not be able to be garbage collected until the collection itself goes out of scope.  In fact, any event subscriber will not be able to be garbage collected until the event-generating object goes out of scope.  There are ways to solve this: the WeakEvent pattern is one, but this is usable if you're creating the listener class yourself.  I'll talk about an approach I took to solve this, it's not pretty, it's better than manually unsubscribing.

Posted on Friday, January 18, 2008 3:06 PM WPF , .NET | Back to top


Comments on this post: Understanding Garbage Collection in relation to CollectionView classes

# re: Understanding Garbage Collection in relation to CollectionView classes
Requesting Gravatar...
"I'll talk about an approach I took to solve this, it's not pretty, it's better than manually unsubscribing."

And what would that approach be? I'm quite tired of unsubscribing myself.
Left by Sergi Diaz on Nov 06, 2008 3:31 AM

Your comment:
 (will show your gravatar)


Copyright © Muljadi Budiman | Powered by: GeeksWithBlogs.net