Review of ANTS Memory Profiler

Quick Summary: Highly recommended! This is a big time saver over WinDBG/SOS/CLRProfiler. Either combination will get the job done, but you will complete simple tasks in significantly less time if you choose ANTS Memory Profiler.

The review

Our sponsors over at Red Gate Software released a new version of their Memory Profiler and asked devlicio.us to give it a spin and write up a review. I had never used the tool before, but I have used the standard Microsoft tools to troubleshoot memory issues. My biggest concern with the Red Gate product is not whether it works but if it is worth the price in terms of time saved and productivity. With a list price of US$495 for the standalone product or US$795 for the .NET Bundle, it needs to save a lot of time to be worth the money. To get a feel for how it compares to the Microsoft solution, I decided to run Tess Ferrandez’s .NET Debugging labs 3 and 6 against the Red Gate Memory Profiler. In my evaluation, I’m mostly interested in seeing if I can get to all the data and find the planted bugs without spinning up WinDBG + the SOS CLR extension, AdPlus, and all those other tools.

Setup

My test system is a Dell Studio XPS M13 with 4GB RAM running Windows 7 Ultimate x64.

I installed the 14 day trial of the .NET Bundle. When you download, you will get an email from someone at Red Gate asking how they can help you with your evaluation and reminding you that there are demos and exercises to learn the tool at the bottom of the Memory Profiler home page. I then ran through the demos and labs to get familiar with how the Memory Profiler tool works.

To recreate the problem scenarios, I used Tinyget from the IIS 6.0 Resource Kit Tools.

For setup, I grabbed the BuggyBits.zip file and unzipped it into c:\inetput\wwwroot\BuggyBits. I then opened up Internet Information Services (IIS) Manager, navigated to and right-clicked on the BuggyBits folder, and selected Convert to Application.

Debugging .NET Debugging Demos Lab 3: Memory

This lab stresses a web site and causes a high memory situation. The lab has a simple goal: figure out what caused the issue memory leak. I setup the Memory Profiler to run the BuggyBits application. One thing I ran into was that the profiler won’t actually start profiling until you navigate to another page in the IIS application. For BuggyBits, this meant navigating to http://localhost/BuggyBits/Links.aspx in order to start profiling. I then followed these steps:

  1. Take Memory Snapshot
  2. From a command line, ran tinyget -srv:localhost -uri:/BuggyBits/Links.aspx -loop:400
  3. Took another Memory Snapshot
  4. Started my investigation

I did find that if I ran the application in a loop of 4000 items, Memory Profiler ran into memory issues during the investigation. That said, it was easy enough to do the troubleshooting with a smaller repro case.

The first screen I saw after taking the second snapshot is this:

lab3snapshot

From that, we can see that we have a lot of strings, but we also have two classes with very similar instance counts: StringBuilder and Link. StringBuilder is a .NET CLR type, but Link isn’t. From this same screen, we can also take a look at how many objects are on the Gen 0,1, and 2 heaps. Looking at the Gen 2 heap, we see a similar picture with 3505 Link objects on the heap. What is causing the number of Link objects to be so high? Looking at the source, we see a class with a finalizer declared—the finalizer does some heavy work (simulated via Thread.Sleep):

~Link()

{

    //some long running operation when cleaning up the data

    Thread.Sleep(5000);

}

At this point, we have a few things we can do to fix the code:

  • Eliminate the finalizer altogether.
  • Implement IDisposable and make sure to explicitly Dispose any Link objects after they are used.

I tried both options, and saw that we stopped leaking Link onto any of the heaps.

Discovery in this case was pretty simple as the first screen gave me enough data, via the increase in memory usage on the graph and the analysis to know what was going on as well as inform me if my changes helped.

Debugging .NET Debugging Demos Lab 6:_Memory Leak

Another memory leak situation. I followed a similar setup to the previous example. Steps to gather data are:

  1. Navigate to http://localhost/BuggyBits/ProductInfo.aspx?ProductName=Bugspray
  2. Take Memory Snapshot
  3. From a command line, ran tinyget -srv:localhost -uri:/BuggyBits/ProductInfo.aspx?ProductName=Bugspray –threads:50 -loop:20
  4. Took another Memory Snapshot
  5. Started my investigation

This time, I was greeted by the following summary:

lab6snapshot-1 A large growth in strings still isn’t interesting. This grows due to assemblies being loaded in between calls and is hard to pin as an issue. Looking at “Largest Growth in Instances”, I see that RuntimeType has grown by quite a bit. Let’s see why. I clicked on RuntimeType and saw this screen, showing me how the size had changed:

lab6snapshot-2 I then selected the Class Reference Explorer and got a hint as to what is going on:

 lab6snapshot-3While it doesn’t consume much space, the big hint of System.Xml.Serialization.TypeDesc is enough of a hint that we might have a serialization issue. XmlSerialization generates new assemblies when certain constructors are run. One feature I particularly liked in this investigation was the ability to see what types of objects were holding onto the references of other types. Here is an example of the objects referring to our TypeDesc.

lab6snapshot-4

 

 

 

 

From here, I looked for uses of XmlSerializer and sure enough, found one of the versions that doesn’t cache the serializer. DataLayer.cs contains these lines:

  118         Type[] extraTypes = new Type[1];

  119         extraTypes[0] = typeof(ShippingInfo);

  120 

  121         MemoryStream ms = new MemoryStream();

  122         XmlSerializer xs = new XmlSerializer(typeof(Product), extraTypes);

  123         xs.Serialize(ms, p);

This is one of the constructors that creates a new assembly for every XmlSerializer instance. To fix the issue, one should cache the serializer. I did so by creating a static property on the DataLayer class:

    static XmlSerializer _xs = null;

    static object _xsLock = new object();

    static XmlSerializer Serializer

    {

        get

        {

            if (_xs == null)

            {

                lock (_xsLock)

                {

                    if (_xs == null)

                    {

                        Type[] extraTypes = new Type[1];

                        extraTypes[0] = typeof(ShippingInfo);

                        _xs = new XmlSerializer(typeof(Product), extraTypes);

                    }

                }

            }

            return _xs;

        }

    }

This sped up execution time and reduced the number of RuntimeType instances by 2,031.

lab6snapshot-5

Other Impressions

Given the speed with which I could diagnose the above two issues, I was really impressed. I also loved the ability to dig around and see what classes were in use and track through object graphs. To get the same job done with the Microsoft SDK tools I would have to correlate information from WinDBG + SOS and from CLRProfiler. The fact that I could do all this drill down in one tool gave me a huge productivity boost.

I did have some issues with using the profiler. I posted the issue to the Red Gate forum and had a resolution within a few days. It turned out due to the design of the Server Garbage Collector (the GC that runs on multi-core machines) has a behavior that may appear as memory corruption to a profiler. (You can read the forum thread here.) I had a few other interactions with Red Gate as well and found the company to be very responsive to my needs. Because of the way the tool presents information, I could see patterns quickly and fix the issues with ease. Thanks to the time saving properties, I heartily recommend buying at least one copy of the Memory Profiler for every dev team. This is something I will be using once per sprint to look for memory leaks.


Posted 08-23-2009 11:05 AM by Scott Seely
Filed under: , ,

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)