As I understand it, one of the main causes of memory leaks in C# is failing to unregister event listeners when its container is disposed. For this reason, whenever I manually register an event - such as a Timer.Elapsed += ...
- I make sure to Timer.Elapsed -= ...
when I am done with the object (or parent object).
However, I was just looking over a Windows Form Designer generated class, and noticed that while it happily subscribes to events (e.g. this.button1.Click += new System.EventHandler(this.button1_Click);
), it seems there is no cleanup procedure other than the default components.Dispose();
action.
Does this mean the Dispose()
method of each component is expected to unregister/unsubscribe from any events that have been bound to it; if so, how does the component unregister from 'external' event handlers it is not aware of, and does this mean that manual attempts to remove event listeners from standard [IDisposable] Windows controls (timers, buttons, forms, etc.) is generally unnecessary?
Thanks
The primary tools for detecting memory leaks are the C/C++ debugger and the C Run-time Library (CRT) debug heap functions. The #define statement maps a base version of the CRT heap functions to the corresponding debug version. If you leave out the #define statement, the memory leak dump will be less detailed.
The most common and most easy way to detect is, define a macro say, DEBUG_NEW and use it, along with predefined macros like __FILE__ and __LINE__ to locate the memory leak in your code. These predefined macros tell you the file and line number of memory leaks.
To bring up the window again, click Debug > Windows > Show Diagnostic Tools. Choose Memory Usage with the Select Tools setting on the toolbar. Click Debug / Start Debugging (or Start on the toolbar, or F5). When the app finishes loading, the Summary view of the Diagnostics Tools appears.
Event handlers only cause memory leaks if the object containing the event lives longer than the object containing the handler.
In a typical WinForms scenario, both the controls and the form code only live as long as the form is opened, so there is no problem in the first place.
You only need to unregister your handlers from static events, singletons, or other long-lived objects.
Good design, mostly. The object model was well-crafted to ensure that the event source can't outlive the subscriber. There certainly is a circular reference, the form keep a reference to the control through its Controls collection as well as a possible private variable, the control adds a reference to the form through an event subscription. But the control's lifetime is controlled by the form, both go poof when the user closes the window. That removes the usual one-and-only reference to the form object, kept in an internal table that maps a Handle to a Form. The GC has no trouble with the circular references.
There are a handful of sharp edges, the Application.Idle and the SystemEvents events are trouble. They have plenty of yellow tape in the MSDN Library.
Disposal is automatic as well, not used to unsubscribe events in Winforms, every control disposes the references in its own Controls collection. That starts at the Form class and iterates through the tree automatically. Overriding the form's Dispose() method is unusual, also tends to cause lots of angst because that method is present in the form's Designer.cs file. Moving that method is fine, as is using the FormClosed event to dispose as an alternative.
That has a sharp edge with the byte of a chainsaw though. Disposing controls is not optional in Winforms. Very unusual, it is optional everywhere else in the framework, the finalizer backs-up forgetting to call it. Not in Winforms, if you use Controls.Clear or Remove then the control you remove is not disposed. It is rehosted to a hidden window called the "parking window". Keeping the control alive to move it to another parent. Nice feature, except when you don't move it elsewhere. It will live on that hidden window forever, very nasty leak. Not good design.
There are patterns to solve the lifetime problem for events. The "weak event pattern" is fairly boilerplate in .NET programming today. It is a bat-signal sign of an design problem, typically induced by liking the observer pattern because it works so well in .NET but not liking the contract that comes with it. An overbearing object model is almost always the root of the problem, like the three letter acronym whose Name Shall Not Be Mentioned in the [winforms] tag :)
The event source will keep the subscriber around not the other way. When the form goes away it will be eligible for GC which will inturn make the listeners eligible.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With