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:


http://www.codeproject.com/KB/dotnet/Memory_Leak_Detection.aspx


http://blogs.msdn.com/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx


http://blogs.msdn.com/calvin_hsia/archive/2008/04/11/8381838.aspx


http://www.automatedqa.com/techpapers/net_allocation_profiler.asp


http://blogs.msdn.com/greg_schechter/archive/2004/05/27/143605.aspx

Comments (8)

  1. AA says:

    When I try to execute !load sos.dll I get the following message:

    The expression cannot be evaluated while in run mode.

    Do I need to set up something differently? I am running my app in Debug mode.

  2. MSDNArchive says:

    To AA:

    Please set a breakpoint at following line:

    Console.WriteLine("Check memory leak here.");

    When app breaks at this point, you can go ahead to execute !load sos.dll.

    Good luck.

  3. Scott Hagie says:

    Very interesting. Just a note, you need to enable unmanaged debugging for this to work:

    Project Properties/Configuration Properties/Debugging/Enable Unmanaged Debugging

  4. MSDNArchive says:

    Hi Scott – That is right! I’ve updated post with your feedback.

    Thanks.

  5. gOODiDEA.NET says:

    .NET ASP.Net Caching Is Too Easy Writing your first Visual Studio Language Service SharpDevelop (aka

  6. gOODiDEA says:

    .NETASP.NetCachingIsTooEasyWritingyourfirstVisualStudioLanguageServiceSharp…

  7. HuyNguyen says:

    Good post. This post has helped me a lot with the memory leak headache I am encountering with.

    Thanks for your post.