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?
Edit 2014/02/17:
I tried the WeakEventManager(TEventSource, TEventArgs)
and .Net 4.5 and the problem still exists.
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;
}
}
This code compiled using .Net 4.5 do also run out of memory. I got the hint using the Thread.Sleep construct here.
PropertyChangedEventManager
but there's still a memory leak left that could be simply reproduced using the sample code of this question. – Resnick