none
WeakEventManager<TEventSource, TEventArgs> and PropertyChangedEventManager causes memory leak RRS feed

  • Question

  • I've an application where I'm not able to remove event handlers because I don't know when the last reference will be freed.

    My application contains a PropertyChanged event source that is put into a container class that also implements INotifyPropertyChanged. This hierarchy contains more than 6 levels. Each instance of a level could be placed into multiple other instances. That's the reason why I couldn't determine when to free those instances.

    The instances on the lowest level will live for the whole application runtime. This causes that all other instances will not be freed and I got a memory leak.

    To avoid this event driven memory leak I tried to use WeakEventManager(TEventSource, TEventArgs). This class is only available in .Net 4.5 and because of compatibility to existing hardware I’ve to use .Net 4.0.

    In .Net 4.0 is a PropertyChangedEventManager available that should do the same for INotifyPropertyChanged.

    My classes are freed correctly.

    But there is still a memory leak.

    I simplified my application to the following code that produces a memory leak:

    // This code will force the memory leak
    while (true)
    {
        var eventSource = new StateChangedEventSource();
        var eventReceiver = new StateChangedEventReceiver();
    
        PropertyChangedEventManager.AddListener(eventSource, eventReceiver, string.Empty);
    }
    
    public class EventSource : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    }
    
    public class  EventReceiver : IWeakEventListener
    {
        public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            return true;
        }
    }

    Yes I know there is no RemoveListener call. I couldn’t determine when an instance is never used and could be freed. If I knew that I could use normal event registration and deregistration. In that case I don’t have to use the PropertyChangedEventManager.

    What is the problem of my sample code? Why does it produce a memory leak?

    I used JetBrains Memory Profiler to detect my memory leak. I could reduce the memory consumption a lot by using the PropertyChangedEventManager but there's still a memory leak left that could be simply reproduced using the sample code of this question.



    • Edited by Verarind Monday, February 17, 2014 1:03 PM
    Thursday, February 13, 2014 8:30 AM

All replies

  • Hi Verarind,

    I am trying to invite someone familiar with this issue to help you. Thank you for understanding.

    Regards,


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    Friday, February 14, 2014 7:40 AM
    Moderator
  • Hi Verarind,

    I will suggest you have a look at the following article:

    http://blogs.msdn.com/b/nathannesbit/archive/2009/05/28/weakeventmanager-and-iweakeventlistener.aspx

    And we can do more to make sure the listener will be garbage collected:

    1.  Keep a week reference to the event source.

    2. when event source is null, invoke a method to unregister the event hanlder.

    In .net 4.5, WeakEventManager(TEventSource, TEventArgs) is introduced and it almost does the same as I mentioned above.

    Here is also a sample that can be reference:

    http://blog.thekieners.com/2010/02/11/simple-weak-event-listener-for-silverlight/


    <THE CONTENT IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED>
    Thanks
    Alan Yao
    MSDN Community Support

    Please remember to "Mark as Answer" the responses that resolved your issue. It is a common way to recognize those who have helped you, and makes it easier for other visitors to find the resolution later.

    Monday, February 17, 2014 8:08 AM
  • For an event pattern memory leak to occur in the first place, the lifetime of the source object must extend beyond the lifetime of the listener. In your sample code snippet, the liftetime of the source object and the listener are the same so if you just give the garbage collector a chance to collect the objects, there won't be any memory leak:

    while (true)
    {
        System.Threading.Thread.Sleep(10); //pause for 10ms
        var eventSource = new EventSource();
        var eventReceiver = new EventReceiver();
        PropertyChangedEventManager.AddListener(eventSource, eventReceiver, string.Empty);
    }
    

    Monday, February 17, 2014 9:34 AM
  • @Alan_Yao:

    Do you try to tell me that I've to create a background thread that periodically tests for this weak references and calls the RemoveListener method?

    I couldn't do that. In my real project the event source will live forever as mentioned in initial post. The event receiver has to be removed by the garbage collector. Currently there's no pointer available to the event receiver. The WeakEventManager<TEventSource, TEventArgs>.AddListener method signature only takes a delegate. There's no reference to the receiver object. Maybe I'm able to write my own implementation of this class having the same signature and could be removed if we port to .Net 4.5.

    I thought that this is the task of the PropertyChangedWeakEventManager. Maybe I missread the msdn page of this class.

    The only enhancement of .Net 4.5 in my opintion is the availability to weak bind to every possible event. In .Net 4.0 there's only a predefined version for INotifyPropertyChanged and INotifyCollectionChanged. For every other event I've to derive from WeakEventManager and register this class for those Events. I implemented my own WeakEventManager but it also has this memory leak. That's the reason why I tried to use the PropertyChangedEventManager of the framework because I thought I've done anything wrong.

    Monday, February 17, 2014 11:06 AM
  • @Magnus (MM8), @Alan_yao,

    with your hints I wrote a new test program and I used .Net 4.5 to check whether this problem is already existing in .Net 4.5.

    YES IT ALSO EXISTS IN .Net 4.5:

    var eventSource = new EventSource();
    
    var i = 0;
    while (true)
    {
        var eventReceiver = new EventReceiver();
        
        // --> Use only one of the following three lines. Each of them will produce a memory leak.
        WeakEventManager<EventSource, PropertyChangedEventArgs>.AddHandler(eventSource, "PropertyChanged", eventReceiver.OnEvent);
        PropertyChangedEventManager.AddListener(eventSource, eventReceiver, string.Empty);
        WeakEventManager<EventSource, EventArgs>.AddHandler(eventSource, "SomeOtherEvent", eventReceiver.OnSomeOtherEvent);
        // <--
    
        ++i;
        if (i == 1 << 18)
        {
            Thread.Sleep(10);
            GC.Collect(2);
            Thread.Sleep(10);
            i = 0;
        }
    }
    
    
    public class EventSource : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        public event EventHandler<EventArgs> SomeOtherEvent;
    }
    
    public class EventReceiver : IWeakEventListener
    {
        public void OnSomeOtherEvent(object sender, EventArgs args)
        {
        }
    
        public void OnEvent(object sender, PropertyChangedEventArgs args)
        {
        }
    
        public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            return true;
        }
    }

    I'm frustrated. The WeakEventManager<TEventSource, TEventArgs> will also have this memory leak. That's annoying.

    Please help me to fix this memory leak.

    Currently I'm trying to write my own WeakEventManager<TEventSource, TEventArgs> class that won't have a memory leak. I hoped that I don't have to do that.

    I found new information about the memory leak of the WeakEventManager:

    http://social.msdn.microsoft.com/Forums/vstudio/en-US/58431b33-7436-4880-8019-ee7b0a5d63db/addlistener-of-weakeventmanager-consumes-memory?forum=netfxbcl

    I've found something realy annoying: The following post tells me that the WeakEventManager<.., ..> is only able to free the memory on application that have a WPF message loop. I'm using WinForms. 

    http://www.codeproject.com/Articles/29922/Weak-Events-in-C#heading0011

    The msdn page of WeakEventManager<.., ..> doesn't tell any word about that.

    • Edited by Verarind Monday, February 17, 2014 1:44 PM New information found.
    Monday, February 17, 2014 1:01 PM
  • If you leak memory I would check if your subscription call happens on a UI thread with a message pump. If not you will get desktop heap exhaustion bugs. For more information why the WPF event manager on non UI threads is a bad idea can be found here: http://geekswithblogs.net/akraus1/archive/2014/02/04/155370.aspx. Your code with the WeakEventManager should just work. I would try out the FastSmartWeakEvent which is one small cs file which has no bugs and has even a better syntax and less bugs: http://www.codeproject.com/Articles/29922/Weak-Events-in-C#heading0014


    • Edited by Alois Thursday, February 20, 2014 6:09 PM
    Thursday, February 20, 2014 6:09 PM
  • @Alois:

    I tried my code using a WPF .Net 4.5 application and the memory leak an I still run out of memory. There's no word in the msdn page of the WeakEventManager that it will only work in a WPF application. I'm using WinForms - that might be a reason why I got this memory problem but the plain WPF application has the same behavior.

    I had a look on the FastSmartWeakEvent might work but I liked the way that the sender of the event doesn't know whether the receiver will use the event as a normal event or a weak event. In some cases it's very useful to bind to the event without having any other reference (like a bind-and-forget event). This won't work with this FastSmartWeakEvent.

    I wrote my own WeakEventManager that uses weak references and dynamic code to bind, unbind and raise the event. I also added a background thread that periodically clears all dead weak references out of the list of items. This is working fine and is fast enough (for my cases).

    Maybe there will be a simpler solution in the future or the WeakEventManager will work as expected. I created my WeakEventManager having the same method signature for AddHandler and RemoveHandler. This should let me simply change to the one provided by the framework if it works in the future.

    Monday, February 24, 2014 9:34 AM