Memory Leaks Demo & Detection in .NET Application
Memory leaks are always headache of developers. Do .NET developers no longer bother to worry about memory leaks because of garbage collection? Yes and NO. GC periodically find objects that cannot be accessed in the future and then reclaim the resources used by the objects. GC achieves this by maintaining a list of references to live objects. When this mechanism is broken, memory leak happens.
There are many reasons to leak memory. In addition to calling unmanaged code from managed code, another one of general cases is about event handler. If you do this:
Foo.FooEvent += new EventHandler(MemoryLeaksHere.Method);
When you complete using MemoryLeaksHere, but you are still using Foo, then MemoryLeaksHere will still remain alive as well. MemoryLeaksHere object will leak memory as a result of failing to GC.
Let us take a look at one simple example first.
using System; namespace MemoryLeakSample { class Foo { public static Foo myFoo; public event EventHandler FooEvent; public Foo() { myFoo = this; } public void FooMethod() { MemoryLeaksHere memLeak = new MemoryLeaksHere(); memLeak.TryQuit(); } public void FireEvent() { FooEvent(null, null); } static void Main(string[] args) { Foo foo = new Foo(); for (int i = 0; i < 5; ++i) { foo.FooMethod(); } GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); Console.WriteLine("Check memory leak here."); } } /// <summary> /// This object will cause memory leak /// </summary> public class MemoryLeaksHere { public MemoryLeaksHere() { Foo.myFoo.FooEvent += new EventHandler(OnMyFooEventFired); Console.WriteLine("\nObject-{0}: Construct. Subscribe.", this.GetHashCode()); } ~MemoryLeaksHere() { Console.WriteLine("Object-{0}: Deconstruct.", this.GetHashCode()); } public void TryQuit() { Console.Write("Object-{0}: leak me?", this.GetHashCode()); string input = Console.ReadLine(); if (string.Equals(input, "no")) { Foo.myFoo.FooEvent -= new EventHandler(OnMyFooEventFired); Console.WriteLine("Object-{0}: Unsubscribe.", this.GetHashCode()); } else { Console.WriteLine("Object-{0}: Not Unsubscribe", this.GetHashCode()); } } private void OnMyFooEventFired(object sender, EventArgs e) { // Do something } } } |
In MemoryLeaksHere object’s constructor, Foo starts to hold a reference to MemoryLeaksHere by registering event handler. In MemoryLeaksHere.TryQuit(), if we don't unregister, memory leak will happen.
To be more intuitive, you can copy/paste sample code to VS2008, and then enable unmanged code debugging by following:
Project->Properties->Debug->Enable Unmanaged Code debugging
Now set a breakpoint at “Check memory leak here”, and start build/debug. When being asked leak me or not, you can choose either yes or no. For example:
Here, looks like we leak two of them. Finally app will hit the breakpoint and stop. At this point, we can go to VS immedate window to load sos.dll, and then check how many objects in the heap:
!load sos.dll extension C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded !dumpheap -type MemoryLeaksHere PDB symbol for mscorwks.dll not loaded Address MT Size 0132e7d0 00983104 12 0132eba0 00983104 12 total 2 objects Statistics: MT Count TotalSize Class Name 00983104 2 24 MemoryLeakSample.MemoryLeaksHere Total 2 objects |
So now we know there are two object instances are not recycled. Why are they not GC-ed? Because someone has a reference to them. Choose one of them, and use gcroot command.
!gcroot 0132e7d0 Note: Roots found on stacks may be false positives. Run "!help gcroot" for more info. Error during command: Warning. Extension is using a callback which Visual Studio does not implement. Scan Thread 7592 OSTHread 1da8 ESP:12f434:Root:01312d48(MemoryLeakSample.Foo)-> 0132f704(System.EventHandler)-> 0132f6ec(System.Object[])-> 0132e7dc(System.EventHandler)-> 0132e7d0(MemoryLeakSample.MemoryLeaksHere) Scan Thread 4704 OSTHread 1260 |
Now we can see that MemoryLeakSample.Foo is still referencing MemoryLeakSample.MemoryLeaksHere via event handler. If it is not 5 iterations, image what would happen if every incoming request results in a slice of memory leak... Soon or later, you online service will be down.
See also:
https://www.codeproject.com/KB/dotnet/Memory_Leak_Detection.aspx
https://blogs.msdn.com/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx
https://blogs.msdn.com/calvin_hsia/archive/2008/04/11/8381838.aspx
https://www.automatedqa.com/techpapers/net_allocation_profiler.asp
https://blogs.msdn.com/greg_schechter/archive/2004/05/27/143605.aspx
Comments
- Anonymous
February 10, 2009
PingBack from http://blog.a-foton.ru/index.php/2009/02/11/memory-leaks-demo-detection-in-net-application/ - Anonymous
February 10, 2009
The comment has been removed - Anonymous
February 11, 2009
The comment has been removed - Anonymous
February 12, 2009
Very interesting. Just a note, you need to enable unmanaged debugging for this to work:Project Properties/Configuration Properties/Debugging/Enable Unmanaged Debugging - Anonymous
February 12, 2009
Hi Scott - That is right! I've updated post with your feedback.Thanks. - Anonymous
February 14, 2009
.NET ASP.Net Caching Is Too Easy Writing your first Visual Studio Language Service SharpDevelop (aka - Anonymous
February 14, 2009
.NETASP.NetCachingIsTooEasyWritingyourfirstVisualStudioLanguageServiceSharp... - Anonymous
May 04, 2009
Good post. This post has helped me a lot with the memory leak headache I am encountering with.Thanks for your post.