One of the major problems that developers face today is the memory leaks in an applications. Often the software that is built looks great but does not follow basic guidelines to ensure that the application is not memory hungry or even there are no existing memory leaks.
Note : By memory leak we mean, some portion of memory is not reclaimed by the garbage collector even though the object is not in use.
By this way, the memory usage of the application increases at a certain extent and finally crashes with the OutOfMemoryException. To detect a memory leak there exists a large number of tools of which some are free while others are used as commercial purposes. Most of these problems can be fixed by either using Disposable pattern ( IDisposable interface) or manually de - referencing each and every object that are not in use. Sometimes, this can be also done in destructor / finalizers in .net too (but remember using destructors lose GC cycle).
GC algorithm actually tries to find all reachable objects in the entire heap and creates a map. It then puts the reachable objects one by one into the heap releasing memory of all unreachable objects. This is called Heap Compaction. By reachable objects we mean those objects to which the application still has strong reference, and the items which can be called in future. Thus it is important to release any strong reference of a memory when it is not in use, so that GC can collect it.
Even though Garbage Collector separates all the reachable objects with non-reachables, there is one exception to this. .NET framework exposes WeakReference as a special type in the system, which can hold objects that are reachable but still it will be collected by the garbage collector. You can look into it in detail in my post : Garbage Collection Algorithm with the use of WeakReference
The Problem
Now coming back to the application, sometimes it is even difficult to identify a memory leak when it exists inside of its framework. One of such scenario is when a large portion of memory is held by the EventHandler where the source has an EventHandler which is connected with a strong reference of the target which has the event. Let me explain it more :
Object A holding strong reference of Object B through the use of EventHandler |
A a1 = new A(); a1.PropertyChanged += new PropertyChangedEventHandler(this.A_PropertyChanged);
So now object A can call A_PropertyChanged event handler defined inside Object B directly.
At a certain point, lets say Object A is no longer in use and is de-referenced from B by doing :
a1 = null;
Here the de-referencing works good, but you are missing the fact that a1 still holds reference of B through the Event PropertyChanged, and will not be GCed until B is de-referenced as well. Here is the problem of memory leak. If B is a Static object and creates lots of A throughout the program, this problem can really leak lots of memory and eventually can load the process to crash.
The Solution
To deal with such a problem, we need a Co-ordinator for the Event such that the event raised by the object does not go directly to the Object B, rather it calls an intermediate listener which routes the event to the handler of Object B.
The Object A is not communicating directly with Object B, rather it connects with a WeakListener which exposes one WeakReference object to hold B |
In the above figure, we can see, the Listener co-ordinates the event with the EventHandler thus de-referencing Object A and Listener from Object B will remove all the references from it. Lets implement this approach using code.
interface IPropertyChanged { void OnPropertyChanged(object sender, PropertyChangedEventArgs e); } public class WeakEventListener : IDisposable { readonly object locker = new object(); WeakReference weakListener; INotifyPropertyChanged target; IPropertyChanged source; public WeakEventListener(IPropertyChanged source, INotifyPropertyChanged target) { this.source = source; this.target = target; if (this.target != null) this.target.PropertyChanged += new PropertyChangedEventHandler(target_PropertyChanged); this.weakListener = new WeakReference(source); } void target_PropertyChanged(object sender, PropertyChangedEventArgs e) { object source = null; lock (locker) { if (weakListener != null) { source = weakListener.Target; } if (source == null) { ReleaseListener(); return; } } IPropertyChanged notifier = source as IPropertyChanged; notifier.OnPropertyChanged(sender, e); } private void ReleaseListener() { if (target != null) { target.PropertyChanged -= new PropertyChangedEventHandler(target_PropertyChanged); target = null; weakListener = null; } } #region IDisposable Members public void Dispose() { lock (locker) { this.ReleaseListener(); } } #endregion }So here I have implemented one dummy implementation of WeakEventListener. Here the IPropertyChanged interface is used to reference the object inside the Listener for simplicity.
In the above implementation, you can see, I have used two object. Preferably consider object A in figure with INotifyPropertyChanged object target and IPropertyChanged as object B such that object B creates reference to both INotifyPropertyChanged as well as WeakEventListener. Now if B is derived from interface IPropertyChanged and define the eventhandler inside it, the WeakEventListener can invoke it without holding a strong reference of it.
public class A : INotifyPropertyChanged { string myProperty; public string MyProperty { get { return myProperty; } set { this.myProperty = value; if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("MyProperty")); } } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion } public class B : IPropertyChanged { A a1; WeakEventListener wlistener; public B(A a1) { this.a1 = a1; wlistener = new WeakEventListener(this, a1); } #region IPropertyChanged Members public void OnPropertyChanged(object sender, PropertyChangedEventArgs e) { MessageBox.Show(string.Format("Property {0}, has been changed", e.PropertyName)); } #endregion public void ReleaseA() { if (this.wlistener != null) this.wlistener.Dispose(); this.wlistener = null; this.a1 = null; } }
Now here class A implements INotifyPropertyChanged and invokes it when any of its property is changed. Class B, on the other hand implements IPropertyChanged and provides a eventhandler of PropertyChanged. But as it is using WeakEventListener you can call ReleaseA to release any strong reference of a1 from it and also dispose the listener object. Therefore you can keep on using the object B irrespective of any memory leak been created.
Alternatives inside Framework
WPF has a framework alternative to do the same thing. A number of class are available on the framework which help in implementing the same thing. Lets see how to do this using framework interfaces :
1. Create a class and derive it from WeakEventManager.
2. Implement IWeakEventListener interface to define listener.
3. Use AddListener and RemoveListener to add and remove the objects.
Lets see how I implement the same thing.
class A : INotifyPropertyChanged { string myProperty; public string MyProperty { get { return myProperty; } set { this.myProperty = value; if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs("MyProperty")); } } //code to raise the event public event PropertyChangedEventHandler PropertyChanged; } class B : IWeakEventListener { A a1; public B(A a1) { this.a1 = a1; PropertyChangedEventManager.AddListener(a1, this, "MyProperty"); } public bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { PropertyChangedEventArgs pe = e as PropertyChangedEventArgs; MessageBox.Show(string.Format("Property {0}, has been changed", pe.PropertyName)); return true; } public void ReleaseA() { PropertyChangedEventManager.RemoveListener(this.a1, this, "MyProperty"); this.a1 = null; } }
Here the PropertyChangedEventManager is a special class derived from Dispatcher object deals with INPC objects by holding a collection of all those INPC in the whole process and calling appropriate objects when event is raised. Thus ReceiveWeakEvent will be called, just like what we did earlier in B when A raises the event provided you implement IWeakEventListener interface on it.
The ReleaseA is used to release the object A from memory without any strong reference been held.
There is also a CollectionChangedEventManager to deal with ObservableCollection events or even you can build your own by deriving from WeakEventManager.
I hope the post will help you. Please let me know your thoughts.
Thanks for reading.
No comments:
Post a Comment
Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.