Event registrations in C# (and .NET in general) are the most common cause of memory leaks. At least from my experience. In fact, I saw so much memory leaks from events that seeing += in code immediately makes me suspicious.
While events are great, they are also dangerous. Causing a memory leak is very easy with events if you don’t know what to look for. In this post, I’ll explain the root cause of this problem and give several best practice techniques to deal with it. In the end, I’ll show you an easy trick to find out if you indeed have a memory leak.
Understanding Memory Leaks
In a garbage collected environment, the term memory leaks is a bit
The answer is that with a garbage collector(GC) present, a memory
Let’s see an example:
public class WiFiManager
{
public event EventHandler <WifiEventArgs> WiFiSignalChanged;
// ...
}
public class MyClass
{
public MyClass(WiFiManager wiFiManager)
{
wiFiManager.WiFiSignalChanged += OnWiFiChanged;
}
private void OnWiFiChanged(object sender, WifiEventArgs e)
{
// do something
}
public void SomeOperation(WiFiManager wiFiManager)
{
var myClass = new MyClass(wiFiManager);
myClass.DoSomething();
//... myClass is not used again
}
In this example, let’s assume WiFiManager is alive throughout the lifetime of the program. After executing SomeOperation, an instance of MyClass is created and never used again. The programmer might think the GC will collect it, but not so. The WiFiManager holds a reference to MyClass in its event WiFiSignalChanged and it causes a memory leak. The GC will never collect MyClass.
1. Make sure to Unsubscribe
The obvious solution (although not always the easiest) is to remember to unregister your event handler from the event. One way to do it is to implement IDisposable:
public class MyClass : IDisposable
{
private readonly WiFiManager _wiFiManager;
public MyClass(WiFiManager wiFiManager)
{
_wiFiManager = wiFiManager;
_wiFiManager.WiFiSignalChanged += OnWiFiChanged;
}
public void Dispose()
{
_wiFiManager.WiFiSignalChanged -= OnWiFiChanged;
}
private void OnWiFiChanged(object sender, WifiEventArgs e)
{
// do something
}
Of course, you’d have to make sure to call Dispose. If you have a WPF Control, an easy solution is to unsubscribe in the Unloaded event.
public partial class MyUserControl : UserControl
{
public MyUserControl(WiFiManager wiFiManager)
{
InitializeComponent();
this.Loaded += (sender, args) => wiFiManager.WiFiSignalChanged += OnWiFiChanged;
this.Unloaded += (sender, args) => wiFiManager.WiFiSignalChanged -= OnWiFiChanged;
}
private void OnWiFiChanged(object sender, WifiEventArgs e)
{
// do something
}
}
Pros: Simple, Readable code.
Cons: You can easily forget to unsubscribe, or won’t unsubscribe in all cases which will lead to memory leaks.
NOTE: Not all event registrations cause memory leaks. When registering to an event that you will outlive, there will be no memory leak. For example, in a WPF UserControl you might register to a Button’s Click event. This is fine and there’s no need to unregister since the User Control is the only one referencing that Button. When there’s no-one referencing the User Control, there will be no-one referencing the Button as well and the GC will collect both.
2. Have handlers that unsubscribe themselves
In some cases, you may want to have your event handler to occur just once. In that case, you’ll want the code to unsubscribe himself. When your event handler is a named method, it’s easy enough:
public class MyClass
{
private readonly WiFiManager _wiFiManager;
public MyClass(WiFiManager wiFiManager)
{
_wiFiManager = wiFiManager;
_wiFiManager.WiFiSignalChanged += OnWiFiChanged;
}
private void OnWiFiChanged(object sender, WifiEventArgs e)
{
// do something
_wiFiManager.WiFiSignalChanged -= OnWiFiChanged;
}
}
However, sometimes you’d want your event handler to be a lambda expression. In that case, here’s a useful technique to have it unsubscribe itself:
public class MyClass
{
public MyClass(WiFiManager wiFiManager)
{
var someObject = GetSomeObject();
EventHandler<WifiEventArgs> handler = null;
handler = (sender, args) =>
{
Console.WriteLine(someObject);
wiFiManager.WiFiSignalChanged -= handler;
};
wiFiManager.WiFiSignalChanged += handler;
}
}
In the above example, the lambda expression is useful because you can capture the local variable someObject, which you wouldn’t be able to do with a handler method.
Pros: Simple, readable, no chance for memory leaks as long as you’re sure the event will fire at least once.
Cons: Usable only in special cases where you need to handle the event once.
3. Use Weak Events with Event Aggregator
When you reference an object in .NET, you basically tell the GC that object is in use, so don’t collect it. There’s a way to reference an object without actually saying “I’m using it”. This kind of reference is called a Weak Reference. You’re saying instead “I don’t need it, but if it’s still there then I’ll use it”. In
We can use that in several ways to prevent memory leaks. One popular design pattern is to use an Event Aggregator
. The concept is that anyone can subscribe to events of type T, and anyone can also publish events of type T. So when a class publishes the event, all subscribed event handlers will be invoked. The event aggregator references everything using WeakReference. So even if an object
Here’s an example using Prism’s popular event aggregator (available with the NuGet Prism.Core )
public class WiFiManager
{
private readonly IEventAggregator _eventAggregator;
public WiFiManager(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
public void PublishEvent()
{
_eventAggregator.GetEvent<WiFiEvent>().Publish(new WifiEventArgs());
}
public class MyClass
{
public MyClass(IEventAggregator eventAggregator)
{
eventAggregator.GetEvent<WiFiEvent>().Subscribe(OnWiFiChanged);
}
private void OnWiFiChanged(WifiEventArgs args)
{
// do something
}
public class WiFiEvent : PubSubEvent<WifiEventArgs>
{
// ...
}
Pros: Prevents memory leaks, relatively easy to use.
Cons: Acts as a global container for all events. Anyone can subscribe to anyone else. This makes the system hard to understand when overused. No separation of concerns.
4. Use Weak Event Handler with regular events
With some code acrobatics, it’s possible to utilize Weak Reference with regular events. This can be achieved in several different ways. Here’s an example using Paul Stovell’s WeakEventHandler :
public class MyClass
{
public MyClass(WiFiManager wiFiManager)
{
wiFiManager.WiFiSignalChanged += new WeakEventHandler<WifiEventArgs>(OnWiFiChanged).Handler;
}
private void OnWiFiChanged(object sender, WifiEventArgs e)
{
// do something
}
}
public class WiFiManager
{
public event EventHandler<WifiEventArgs> WiFiSignalChanged;
// ...
public void SomeOperation(WiFiManager wiFiManager)
{
var myClass = new MyClass(wiFiManager);
myClass.DoSomething();
//... myClass is not used again
}
I really like this approach because the publisher, WiFiManager in our case, is kept with standard C# events. This is just one implementation of this pattern, but there are actually many ways you can go about it. Daniel Grunwald wrote an extensive article about the different implementations and their differences.
Pros: Utilizes standard events. Simple. No memory leaks. Separation of concerns (unlike Event Aggregator).
Cons: The different implementations of this pattern have subtleties and different problems. The implementation in the example actually creates a wrapper object registered that is never collected by the GC. Other implmentations can solve this, but have other issues like additional boilerplate code. See more information on this in Daniel’s article .
Problems with the WeakReference solutions
Using WeakReference means the GC will be able to collect the subscribing class when possible. However, the GC doesn’t collect unreferenced objects immediately. It does randomly as far as the developer is concerned. So with weak events, you might have event handlers invoked in objects that shouldn’t exist at that point.
The event handler might do something harmless like update inner state. Or it might change the program state until some random time the GC decides to collect it. This type of behavior is indeed dangerous. Additional reading on this in The Weak Event Pattern is Dangerous .
5. Detecting memory leaks without a memory profiler
This technique is to test for existing memory leaks, rather than coding patterns to avoid them in the first place.
Suppose you suspect a certain class has a memory leak. If you have a scenario where you create one instance and then expect the GC to collect it, you can easily find out if your instances will be collected or if you have a memory leak. Follow these steps:
1.Add a Finalizer to your suspect class and place a breakpoint inside:
- Add these magic 3 lines to be called in the start of the scenario:
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
This will force the GC to collect all unreferenced instances (don’t use in production) up to now, so they won’t interfere with our debugging.
-
Add the same 3 magic lines of code to run after the scenario. Remember, the scenario is the one where your suspect object is created and should be collected.
-
Run the scenario in question.
In step 1, I told you to place a breakpoint in the class’s Finalizer. You should actually pay attention to that breakpoint after the first garbage collection’s finished. Otherwise you might be confused with older instances being disposed. The important moment to notice is whether the debugger stopped in the Finalizer after your scenario.
It helps to also place a breakpoint in the class’s constructor. This way you can count how many times it was created versus how many times it was finalized. If the breakpoint in the finalizer was triggered, then the GC collected your instance and everything’s fine. If it didn’t, then you have a memory leak.
Here’s me debugging a scenario that uses WeakEventHandler from the last technique and doesn’t have a memory leak:
Here’s another scenario where I use regular event registration and it does have a memory leak:
Summary
It always amazes me how C# might seem like an easy language to learn, with an environment that provides training wheels. but in reality, it’s far from it. A simple thing like using events can easily turn your application to a bundle of memory leaks by an untrained hand.
As far as the correct pattern to use in code, I think the conclusion from this article should be that there is no right and wrong answer for all scenarios. All provided techniques, and the variations of
This turned out to be a relatively big post, and yet I stayed at a relatively high level with this issue. This just proves how much depth exists in these matters and how software development never ceases to be interesting.
For more information on memory leaks, check out my article Find, Fix, and Avoid Memory Leaks in C# .NET: 8 Best Practices . It’s got tons of information from my own experience and other senior .NET developers that advised me for it. It includes info on Memory Profilers, Memory leaks from unmanaged code, monitoring memory and more.
I’d love you to leave some feedback in the comments section. And be sure to subscribe to the blog and be notified of new posts.
What about using Reactive Extensions?
I actually considered including them. I love Rx but I don't know any advantages they provide to avoid memory leaks. Do you have any insight regarding that?
Rx.Net comes with a set of handy System.IDisposable implementations and patterns. Here are a few:
SerialDisposable - has a Disposable property that can be assigned multiple times. Every time a new assignment is made, the old IDisposable will be disposed. This is pretty handy when you have to react to changes, for example, you might receive multiple objects during runtime, and each time you'll have to unsubscribe from the last object and subscribe to the new one.
CompositeDisposable - is a IDisposable object that is also a container of other IDisposable objects. When the Dispose method is called, it'll also dispose the contained IDisposables.
Disposable.Create - this method accepts a lambda (Action) and wraps it in an IDisposable implementation.
Disposable.Empty - returns an IDisposable object that does nothing when Dispose is called - you can use it as a null-object pattern, where you can call Dispose safely, without worrying about nulls. I like to use it with SerialDisposable. For example, with Interactivity.Behaviors (WPF/UWP), in OnAttached I'll assign it an actual disposable object and in OnDeattaching, I'll assign it Disposable.Empty.
//code
public override void OnAttached()
{
//Subscribe
_serialDisposable.Disposable = new CompositeDisposable
{
Observable.FromEventPattern(AssociatedObject, nameof(AssociatedObject.SvgChanged)).Subscribe(SvgChanged),
};
}
public override void OnDeattaching()
{
// Unsubscribe and free up resources.
_serialDisposable.Disposable = Disposable.Empty;
}
Awesome information Moaid, Thanks!
So the reason I didn't include Rx is that it provides great patterns to avoid memory leaks pattern IF you are using Rx.
If you're using regular events, then Rx doesn't really provide an alternative. Well, it provides a nicer Dispose pattern but you can just as easily unsubscribe your regular event from a Dispose method.
Anyway, I'll sleep on it some and maybe add Rx part as well.
It is trivial to convert events into an IObservable stream https://rehansaeed.com/reac...
It's well worth the effort for the composability of the consumption as well as the disposal of the event stream.
Thanks for the beginning of your summary. As a game programming teacher I have been preaching that for years. C# has this image of being a good beginners' language because it hides so many complicated concepts, but it's cases like this that show that it doesn't hide and solve them, but merely hides information that you will eventually need to understand.
What always amazes me is how much we programmers rely on good naming, have to come up with good naming every day, yet so often suck at it. Granted this isn't such a bad case as the famous "memoizing" or "trie" naming fails, but still why do many programmers call this a memory leak when it's distinctly different? At no point in your examples did memory leak, you merely lost a reference that would allow you to selectively unsubscribe one callback from the multicast event, but since clearing the event altogether would free the memory imho it's not a memory leak.
That's why I prefer the term reference leak, because it's the subscriber's reference that was lost, but the reference inside the event is still there, so the memory is kept alive for a reason.
Especially if you plan to write a larger article that includes unmanaged code I would seriously consider making a distinction between the two, otherwise you would be contributing to the deceptive oversimplification that you complained about in your summary.
Hi Lars,
Awesome, do you teach Unity?
Good point! I agree with you that there is a distinction between the managed language "Reference Leak", and the classic "C++" memory leak where you allocate memory, then the pointer to that memory is gone and there's just no way to free it.
It's matter of definition I guess. I could claim that the term "Memory Leak" can include both "Reference Leak" and classic memory leak, and it's a matter of context. So in a managed environment, a "Memory Leak" will mean both of these things.
That is confusing and inaccurate at times, but it seems to be the definition most developers use these days.
In my bigger article I do address this distinction in the intro, and I hope it will be clear enough for the readers.
Michael,
In your example of the IDisposable implementation couldn't you just create a Finalizer which would call the class's own Dispose method, or was this implied in your example? Wouldn't this ensure that Dispose was always called? Do you have any examples of when it would NOT be called?
This has been my typical pattern (based on MS's recommend IDisposable pattern), so if there is anything wrong with it I would very much like to know.
Thanks,
Tim
Hi Tim,
You are right that the standard Dispose pattern does that. For *managed memory leaks that are presented in this article its less relevant. If an object is still referenced with an event the finalizer will never be called.
The dispose pattern is useful when your object created *native resources(with pInvoke maybe) and needs to dispose of them. I'll talk about it in my upcoming post :)
Hi Tim,
In addition to Michael's answer- When the GC collects an object that has a finalizer, it will not release it resources immediately. Instead, it will schedule it to be released on the F-Reachable-Queue. This has some consequences:
Generation 2 is collected about 10 times less than generation 1 that is also collected about 10 times less that generation 0.
In addition - the CLR (per process) allocates one dedicated thread for iterating over the F-Reachable-Queue. If an exception is thrown during the code of a finalizer (for example dereferencing a managed object that was already released), the thread will fault and no other finalizers will be called (eventually will cause a crash).
Why you didn't mention WeakEventManagerClass? (https://docs.microsoft.com/...)
Hi Kanis,
It's one of the implementations for Weak Handlers. I wanted to give the simplest example. It's mentioned as an implementation option in the linked article by Daniel Grunwald(https://www.codeproject.com...)
Have you notice that detection method from point number 5 does not work for .Net Core (2.1.502)? Precisely, debugger do not enter the finalizer code - ever :-). It looks like the finalizer is executed only in release mode, not debug one. Nevertheless this is great post from which I learned a lot. Thank you!
I checked it now and you are indeed correct, Thanks for the tip!
Thanks for sharing! I learned a lot!
I also found a case when the finalizer of a garbage collected object wasn't called. It's when the class is a UserControl. There's a discussion of this on StackOverflow: https://stackoverflow.com/q... . The basic idea is that a standard implementation of the Dispose pattern (like with the UserControl class) includes a GC.SuppressFinalizer() call (to prevent releasing resources 2x). This can be fixed by overriding Dispose() and, after calling the base method, add a call to GC.ReRegisterForFinalize(this).
Why would you want to change this behavior? Finalizers are expansive, better to suppress them if they're not needed?
For the detection method from point number 5.
Oh... I see what you're saying. For that you can use a different method with "Make object ID". Check out item 4 in a different article of mine https://wordpress-245057-75...