Thursday, 23 February 2017

Memory Leak Detection using Redgate ANTS Memory Profiler

For High-Availability (HA) software applications, it is important to ensure that there are no memory leaks. I would like to share my experiences with an useful memory leak detection tool for C#. This is the ANTS Memory Profiler from Redgate. This tool is able to help you track down the source of the leaks.

To explain how the tool detects leaks, I am using our C# application that has a subtle memory leak. 
It has a Master object which communicates with his Worker objects using an event. We know there is a leak because after every time we create a Worker object and destroy it again, our memory usage increases.

Code Listing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Master
{
    public event EventHandler Report;
    private void RaiseEvent()
    {
        EventHandler handler = Report; //publisher-subscriber idiom
        if (handler != null)
        {
            handler(this, new EventArgs());
        }
    }

}
public class Worker
{
    private byte[] data;

    public Worker(Master p)
    {
        data = new byte[10000];
        p.Report += OnReport;
    }
    private void OnReport(object sender, EventArgs e) //event handler
    {//report status
    }
}
class Program
{
    private static Master p = new Master();
    public static void Main(string[] args)
    {
        Console.WriteLine("Do first ANTS snapshot here, then press any key to continue...");
        Console.ReadKey(true);
        for (int i = 0; i <= 1000; i++)
        {
            Worker w = new Worker(p);
            UseThenDispose(w);
        }
        Console.WriteLine("Do second ANTS snapshot here, then press any key to continue...");
        Console.ReadKey(true);

    }
    private static void UseThenDispose(Worker w)
    {
        Console.WriteLine("Worker doing some work ...");
        w = null; //free the memory
        Console.WriteLine("Worker's resources are disposed.");
    }
}

Starting ANTS

When you open the profiler (choosing a "New Profiling Session"), you see the Startup screen:

All we need to do is point it at our C# application (Worker.exe), and click "Start profiling".
The profiler starts up Worker.exe and begins collecting performance counter data:

Taking Memory Snapshots

Taking and comparing memory snapshots is a key activity when looking for memory leaks, so our approach will be as follows:

1. Wait for memory usage to stabilize, then take the first snapshot by clicking on the Take Memory Snapshot button near the top right hand corner. This first snapshot will be used as a baseline.

When we click the Take Memory Snapshot button, the memory profiler forces a full garbage collection and takes a snapshot of the memory it is using (as seen below).


In our code, the memory usage stabilizes at line 32. For ease of understanding, at this juncture the code writes a message to the console window.

2. Then perform the actions that we think cause the memory leak.
In our code, memory leaks have occurred by the time when we arrive at line 39. Again for ease of understanding, at this juncture the code writes a message to the console window.
If you take a careful look at the memory graph below, you can see a faint green line, which shows that the Private Bytes memory usage has increased, so take a second snapshot. The next section explains what is meant by Private Bytes.
ANTS is able to plot different types of memory usage simultaneously. Examples of the types of memory usages seen in the diagram above are as follows
  • Private Bytes: Includes memory allocated (even if not in use), but excludes shared processes (e.g. DLL in memory and .NET run-time).
  • Working Set: Includes shared processes as well. 

3. Examine the comparison that the profiler shows us after it has finished taking and analyzing the second snapshot. This will be further explained in the next section.

Comparison of Memory Usage


The ANTS summary screen above shows a lot of information which I will explain one by one. You can follow along by looking at the octagon-shaped labels inside the summary screen.

  1. We can see a large memory increase between snapshots: 9.7 MB vs 52 kB.
  2. The largest classes are shown to us in the bottom right of the screen: Byte[] and EventHandler.
  3. Next, we switch to the Class List to find out more. The class list gives us a fuller picture of what's in the snapshot.

Class List


We're interested in objects which have been created since the baseline snapshot, so we can look at classes which have increased in size in the second snapshot. We therefore sort by Size Diff in decreasing order.

The Byte[] class has been placed at the top of the list, with an increase of over 10 million bytes. We want to understand why there is such a large increase, so we load the Instance Categorizer for the Byte[] class by clicking the Instance retention graph iconicon.

Instance Categorizer



We can now see the source of the leaks:
  1. The Byte[] arrays belong to the Worker objects
  2. The Worker objects were not disposed at all because the EventHandler in the Master object was still holding on to them. This is found in line 21 of the code, where the Worker objects are still subscribed to the Master object's event. 
Having discovered the source, it is now easy to remove the leak. You just have to unsubscribe from the event at the time of disposal of each Worker. 

Summary

There are many other features inside ANTS which I did not have to use today because I have already found the source of the leaks. If you like, you can try out ANTS yourself. There is a 2-week trial version at Redgate's website.

All in all, I like the ANTS tool because it has helped me to solve hard-to-understand leaks in the C# projects that I was involved in. It is good for both C# executables and DLLs. I have also used it successfully with managed C++, since the tool supports the .NET development environment.

Sunday, 5 February 2017

Memory Leaks in C#

Some people wrongly assume that there are no memory leaks for C# applications since .NET has an automatic garbage collector. But there is an easy way to understand how memory leaks can still occur.

How? Just consider the rate of change in memory over time for a C# application. As long as the rate of memory allocation is faster than the rate of memory recovery (which is what the garbage collector does), all our available memory will eventually be allocated.

As proof, look at the following memory profile of a C# application that is leaking memory at a rate of 100K bytes per second.



Code Listing

Here is the code with the leak:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
    class Program
    {
   
        private static Queue queue = new Queue();
        static void Main(string[] args)
        {
            int factor = 100;
            int i=0;
            do
            {
                Thread.Sleep(1000);
                var v = new byte[factor * 1024];
                queue.Enqueue(v);
                v = null;
                Console.WriteLine("i={0}", ++i);
            } while (true);
         
        }
    }


Notice line 14 where the programmer wanted to let the Garbage Collector (GC) know that it is alright to reclaim the object by setting its value to null. But the GC will not be able to do so because there is still another reference to the object inside the queue container.

When an container holds references longer than necessary, memory consumption will increase and (in this example) eventually results in an OutOfMemoryException.

In a future post, I hope to be able to show a more complicated memory leak that occurs with event handlers.