TN_1202: Using the Visual Studio Team System Profiler: Object Allocation and Object Lifetime views
Ian Huff, Software Design Engineer
Microsoft Corporation
Included with Visual Studio Team System Developer (and Suite) edition is a powerful new profiler for finding performance issues in your native, managed or ASP.NET applications. The profiler can run in both sampling mode (which looks at program state in some periodic cycle) and instrumentation mode (which looks at every function exit and entry point). The performance sessions that the profiler generates have several different views to help you to diagnose performance issues. The profiler can also collect information on the .NET managed objects that are allocated during your program’s run. This TechNote will show you how to enable managed memory tracking and will introduce the two analysis views that can help you to track down memory issues.
For a demo application, I downloaded a rational numbers class off of GotDotNet. This class has a function to create and to factor several large rational numbers. The first step was to add the performance session to the solution. To do this, click the “Performance Wizard…” option under Tools->Performance Tools. This will bring up the wizard where you can select the rational number class as the profiling target and select the profiling method (don’t worry about method for now as we can change it later). On completing the wizard you will see the performance explorer open with the rational number class selected to be profiled. For my profiling scenario I launched the application by clicking the launch button in the top-left of the performance explorer, clicked the performance button (in the launched application, not in the IDE) to launch the performance function above and then I closed the app after the results from the performance function were reported. If you want to, you can download the project and add a performance session to it to follow along with the analysis in this TechNote.
When we normally run the profiler we don’t collect any .NET memory allocation information, and the Allocation and Lifetime views are empty. To enable managed memory tracking just go the properties page for your performance session; on the first page of the properties you can enable managed object allocation tracking and lifetime tracking (to enable lifetime you must also be collecting allocation information). Now if you enable both of these and run the performance session again Allocation and Lifetime views will be populated with information. First off, we’ll take a look at the Allocation view, pictured below.
.jpg)
In the far left column, we see the different object types that were allocated during the profiling run. By expanding this column (as I have done for String and Int32) you can break down the type data by the various functions that allocated that object. The default columns tell you the number of instances created, the total bytes allocated and the percent of total bytes allocated for the entire run. This view can help you pick out objects that are eating up the largest part of your memory, and it can help you to see if your functions are allocating more objects then you intended (a common performance issue with managed code). A good way to start analyzing this data is to sort by percent of total bytes to see what objects are eating up the most space. Also, if you see a large number of allocations coming from a function you can look for ways to trim that function if you own it, or if it is a .NET built in function, see if you can accomplish the same goal with a lighter weight function.
In addition to knowing how many bytes are being used by objects, it can be handy to know how long objects are being held onto in managed applications. In managed code object de-allocation is handled by the built-in garbage collector. So the programmer does not control exactly how long an object is held in memory unless they write their own custom garbage collector (something I don’t recommend unless you have a very specific case that you need to deal with, as the built in garbage collector is very well tuned). The Object Lifetime view (shown below) can help you diagnose issues of this type.
.jpg)
As in Allocation view, the far left column lists the different types of object that were allocated during the profiling run. The next columns (Gen 0 instances collected, Gen 1 instances collected, Gen 2 instances collected, Large object heap instances collected and Instances alive at end) detail how long objects of each type were kept before being garbage collected. Generation zero garbage collections are the garbage collections that happen most frequently, mainly for objects that exist only for a short time. Generation one collections occur less frequently than generation zero and generation two as the least frequent of all. So if you have many large objects being held around until generation two, they are eating up a large portion of memory that you may have not known you were using. A good quick way to check for memory performance issues is to sort by Generation two collections and verify that those items listed should be sticking around for a long time. Also, some objects are de-allocated after the end of the profiling run; those objects are listed under the “instances alive at the end” column. In my example program, the profiling run was so short that all objects were disposed of in generation zero or held until the end, meaning that we really don’t have any lifetime issues for so short of a profiling run. However, for longer running profiling sessions, this type of data can be invaluable.
The Allocation and Object Lifetime views are quality tools for finding some performance errors in managed applications. Automatic garbage collection can be a huge time saver for programmers, but it can also lead to serious performance issues if you do not understand what is going on behind the scenes. The new profiler in Visual Studio Team System can help you to discover exactly what is happening with all of your .NET objects.