1

I have a static class which publishes a few events. So I have many different small classes that have different lifetimes that subscribe to this event.

I found out now that this leads to memory "leaks" because of the subscribing classes staying alive when they subscribed a longer-living event. I know that this is happening by using a memoryprofiler and I read about this problem.

I am not able to manually unsubscribe, as I might have hundreds of "clients" in a list. This list will just get cleared. So I can't (and don't want to) unsubscribe by hand.

I read that the "weak event pattern" might help here. Could someone please lead me to an "easy" way to implement this? All I found until now is either too simple to use in practice or too complicated to understand it in the beginning.

Or is there any "best practice" for this case?

Thanks in advance!

UPDATE: Based on jbl's answer I found this (http://blogs.msdn.com/b/greg_schechter/archive/2004/05/27/143605.aspx) as a possible solution. Any comments here? It's rather old (2004), so there might be better solutions out there?

basti
  • 2,649
  • 3
  • 31
  • 46
  • take a look at this thread, Fredric's explanation to your problem is really helpfull, http://stackoverflow.com/questions/3662842/how-do-events-cause-memory-leaks-in-c-sharp-and-how-do-weak-references-help-miti – Samy S.Rathore Nov 23 '12 at 12:17
  • Have you looked at `WeakEventManager` from WPF (http://msdn.microsoft.com/en-us/library/aa970850.aspx)? – Dennis Nov 23 '12 at 12:20
  • @Dennis: I am using WinForms at the moment. So I am not 100% able to use WPF-Stuff. But I will have a read on the topic. Thanks! – basti Nov 23 '12 at 12:30
  • @chiffre: well, using of `WeakEventManager` doesn't mean "using of WPF", there's no need to render WPF controls. – Dennis Nov 23 '12 at 12:42
  • @dennis: But I would have to reference the WPF-assemblies. Wouldn't I? – basti Nov 23 '12 at 12:49
  • @chiffre: yes, you would. But these assemblies are part of .NET Framework. :) – Dennis Nov 23 '12 at 12:50

2 Answers2

1

Best practice: always implement the Dispose pattern when a class subscribes to an event generated by an object not constructed by this class.

Then in the Dispose method remove the handler.

public NotificationServiceAccessor(ObjectWithEvent objectWithEvent)
{
    _notificationService = new NotificationService();
    _notificationService.StatusChanged += NotificationService_StatusChanged; // Local object, no Dipose

    _objectWithEvent = objectWithEvent;
    _objectWithEvent.AnEvent += AnEventHandler(); // Event that has to be disposed.
}

    #region IDisposable Members

    protected bool Disposed { get; private set; }

    private void Dispose(bool disposing)
    {
        if (!this.Disposed)
        {
            this.InternalDispose(disposing);
        }

        this.Disposed = true;
    }

    protected virtual void InternalDispose(bool disposing)
    {
        if (disposing)
        {
                        // Dispose here the event handlers
                        _objectWithEvent.AnEvent -= AnEventHandler()
        }

        // Dispose here only unmanaged objects 
        // Don’t use managed objects here because maybe 
        // they have been finalized already
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~NotificationServiceAccessor()
    {
        this.Dispose(false);
    }

    #endregion
Ignacio Soler Garcia
  • 21,122
  • 31
  • 128
  • 207
  • Any example implementation for this? – basti Nov 23 '12 at 12:15
  • @SoMoS: completely disagree, that this is a best practice. This is just an approach, which is applicable in some situations. – Dennis Nov 23 '12 at 12:23
  • I thank you for this approach. But I think this will not help me in "any" case. As there are possibilites that the last reference of the subscriber is "nulled" but I can't be 100% sure to call the Dispose-Method. – basti Nov 23 '12 at 12:28
  • @Dennis: ok, I use it always but if you know when it's better to not follow this rule just tell us. – Ignacio Soler Garcia Nov 23 '12 at 12:29
  • There is one problem, I think. If you have a List of subscribers, you will have to iterate over the whole List and call Dispose for every item before being able to clear the list. Otherwise Dispose will never be called and it leaks. – basti Nov 23 '12 at 12:34
  • 1
    @SoMoS: this approach allows to unsubscribe from events only on explicit call of `IDisposable.Dispose`. This, in turn, assumes, that you keep somewhere a reference to disposable object. There are many use cases, when it is either impossible, or not suitable - to keep reference to disposable object. E.g., look at this question - here we have a *static* events, this means, that any piece of code can subscribe to these events during process lifetime. Do you *really* want to track all those disposables? – Dennis Nov 23 '12 at 12:35
1

Never implemented something like that, but I would try (with a static class or a singleton, your choice) :

  • having the static class maintain a static collection of WeakReference to the client event handlers
  • the clients do not subscribe directly to the event. The static class exposes subscribe and unsubscribe methods which add/remove the handlers from the weak references collection
  • the static class is the only one subscribing directly to the event
  • upon event triggering, the static class enumerates the weak references collection and runs the handlers for the references which are still alive (removing the null ones)

Hope this will help

jbl
  • 15,179
  • 3
  • 34
  • 101
  • So you need a WeakEvent-Class for every kind of event? And how do you make sure that the "unsubscribe" is called/done? – basti Nov 23 '12 at 12:29
  • No, the WeakReference is a wrapper around your handler ("somehow" like wrapping it into an Object). http://msdn.microsoft.com/en-us/library/system.weakreference.aspx You won't need to be sure that unsubscribe has been called as the WeakReference does not prevent garbage collection. When the corresponding object has been garbage collected, the content of the weak reference will be null – jbl Nov 23 '12 at 12:38