How To: Use CLR Profiler

 

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

patterns & practices Developer Center

Improving .NET Application Performance and Scalability

J.D. Meier, Srinath Vasireddy, Ashish Babbar, and Alex Mackman
Microsoft Corporation

May 2004

Related Links

Home Page for Improving .NET Application Performance and Scalability

Chapter 5, Improving Managed Code Performance

Send feedback to Scale@microsoft.com

patterns & practices Library

Summary: This How To shows you how to use the CLR Profiler tool to investigate your application's memory allocation profile. You can use CLR Profiler to identify code that causes memory problems, such as memory leaks and excessive or inefficient garbage collection.

Applies To
   CLR Profiler

Contents

Overview
Downloading CLR Profiler
What You Must Know
Profiling Applications
Profiling ASP.NET Applications
Identifying Common Garbage Collection Issues
Identifying Where Your Application Allocates Memory
Analyzing Your Application's Allocation Profile
Sample: ProfilerSample1
Sample: ProfilerSample2
Additional Resources

Overview

CLR Profiler enables you to look at the managed heap of a process and investigate the behavior of the garbage collector. Using the various views in the tool, you can obtain useful information about the execution, allocation, and memory consumption of your application.

CLR Profiler is not a starting point for analyzing problems. Rather, it helps you identify and isolate problematic code and track down memory leaks. Using CLR Profiler, you can identify code that allocates too much memory, causes too many garbage collections, and holds on to memory for too long.

**Note   **CLR Profiler is an intrusive tool that causes your application's performance to be significantly slower than normal (somewhere between 10 to 100 times slower). The tool is not designed for use in production environments.

Downloading CLR Profiler

CLR Profiler is downloaded as a self-extracting executable file. The expanded contents include the source code and the executable file (CLRProfiler.exe). The download also contains a comprehensive document that provides detailed information on CLR Profiler.

Download CLR Profiler

What You Must Know

The primary function of CLR Profiler is to enable you to understand how your application interacts with the managed, garbage-collected heap. Some of the more important things that you can investigate include:

  • Who allocates what on the managed heap.
  • Which objects survive on the managed heap.
  • Who is holding on to objects.
  • What the garbage collector does over the lifetime of your application.

Results of profiling are stored in log files. You can view these files in various ways by using the view menus in CLR Profiler to display the corresponding graphs. Table 1 lists the most useful views.

Table 1: CLR Profiler Views

View Description
Histogram Allocated Types Gives you a high-level view of what object types are allocated (by allocation size) during the lifetime of your application. This view also shows those objects that are allocated in the large object heap (objects larger than 85 KB).

This view allows you to click parts of the graph so that you can see which methods allocated which objects.

Histogram Relocated Types Displays the objects that the garbage collector has moved because they have survived a garbage collection.
Objects By Address Provides a picture of what is on the managed heap at a given time.
Histogram By Age Allows you to see the lifetime of the objects on the managed heap.
Allocation Graph Graphically displays the call stack for how objects were allocated. You can use this view to:

-See the cost of each allocation by method.

-Isolate allocations that you were not expecting.

-View possible excessive allocations by a method.

Assembly, Module, Function, and Class Graph These four views are very similar. They allow you to see which methods pulled in which assemblies, functions, modules, or classes.
Heap Graph Shows you all of the objects in the managed heap, along with their connections.
Call Graph Lets you see which methods call which other methods and how frequently.

You can use this graph to get a feel for the cost of library calls and to determine how many calls are made to methods and which methods are called.

Time Line Displays what the garbage collector does over the lifetime of the application. Use this view to:

-Investigate the behavior of the garbage collector.

-Determine how many garbage collections occur at the three generations (Generation 0, 1, and 2) and how frequently they occur.

-Determine which objects survive garbage collection and are promoted to the next generation.

You can select time points or intervals and right-click to show who allocated memory in the interval.

Call Tree View Provides a text-based, chronological, hierarchical view of your application's execution. Use this view to:

-See what types are allocated and their size.

-See which assemblies are loaded as result of method calls.

-Analyze the use of finalizers, including the number of finalizers executed.

-Identify methods where Close or Dispose has not been implemented or called, thereby causing a bottleneck.

-Analyze allocations that you were not expecting.

Profiling Applications

In this section, you create small sample C# console applications and then profile the applications by using CLR Profiler.

Creating Sample Applications for Profiling

The complete source code for the sample applications is available at the end of this How To. See "Sample: ProfilerSample1" and "Sample: ProfileSample2."

To create sample console applications for profiling

  1. Create a folder named ProfilerSample in which to store the sample code.

  2. In the ProfilerSample folder, create two C# files named ProfilerSample1.cs and ProfileSample2.cs. Copy the sample code from "Sample: ProfilerSample1" and "Sample: ProfileSample2," located at the end of this How To, into the text files.

  3. Open a command window, switch to the ProfilerSample folder, and compile the code by using the following commands.

    csc /t:exe /out:ProfilerSample1.exe ProfilerSample1.cs
    csc /t:exe /out:ProfilerSample2.exe ProfilerSample2.cs
    

Using CLR Profiler to Profile the Application

In this step, you profile ProfilerSample1.exe.

To use CLR Profiler to profile the application

  1. Start CLR Profiler (CLRProfiler.exe)
  2. Make sure that the following check boxes are selected:
    • Profiling active
    • Allocations
    • Calls
  3. Click StartApplication.
  4. In the Open window, navigate to the ProfilerSample folder where you have saved the sample code and select the ProfilerSample1.exe application.
  5. Interact with the application as needed and then close the application.

Profiling ASP.NET Applications

Use the following steps to profile ASP.NET applications.

To profile an ASP.NET application

  1. Start CLR Profiler.

  2. Make sure that the following check boxes are selected:

    • Profiling active
    • Allocations
    • Calls
  3. On the File menu, click Profile ASP.NET.

    CLR Profiler shuts down Internet Information Services (IIS), adds environment variables that are needed for profiling, and restarts IIS. CLR Profiler then prompts you to load the ASP.NET application and waits for the ASP.NET worker process to start.

  4. Use Microsoft Internet Explorer to browse to the ASP.NET application you want to profile.

    You can also run your Web application by using a client tool such as Microsoft Application Center Test (ACT.)

  5. When you have finished running the application, click Kill ASP.NET in the CLR Profiler main window.

    CLR Profiler shuts down IIS, removes the environment variables, and restarts IIS.

    **Note   **Sometimes the current version of the tool does not respond to the page load in Step 4. If this problem occurs, try changing the ASP.NET process identity to SYSTEM in the <ProcessModel> element in Machine.config. After you finish profiling your application, be certain that you change the application's identity back to machine.

Identifying Common Garbage Collection Issues

You can use CLR Profiler.exe to identify and isolate problems related to garbage collection. These include the following memory consumption issues:

  • Excessive allocations
  • Unknown allocations
  • Memory leaks

They also include the following garbage collection issues:

  • Excessive collections
  • Long-lived objects
  • Percentage of time spent performing garbage collection

**Note   **For more detailed information about using CLR Profiler to solve common problems related to garbage collection, see "Common Garbage Collection Problems and How They are Reflected In These Views" in CLRProfiler.doc, which is located in the installation folder of CLR Profiler.

Identifying Where Your Application Allocates Memory

Whenever you are dealing with memory consumption issues, it is very important to know where your application allocates memory.

To identify where your application allocates memory, follow these steps:

  1. Run CLR Profiler on the sample application.
  2. Analyze allocated memory types.
  3. Determine who is allocating the memory.
  4. Evaluate what you can do to reduce the allocations.

Step 1. Run CLR Profiler on the Sample Application

Start CLR Profiler and run the ProfilerSample1.exe application that you created earlier.

Step 2. Analyze Allocated Memory Types

On the View menu, click Histogram Allocated Types. CLR Profiler displays a window similar to the one shown in Figure 1.

Click here for larger image

Figure 1: Histogram Allocated Types view in CLR Profiler

This graph displays objects that have been allocated during the lifetime of the application. In this example, almost 2 gigabytes (GB) of objects have been allocated, nearly all of them strings. The reason is that when you perform string concatenation in the way that the sample code does, the Microsoft .NET Framework allocates a new longer string and copies the old and extended components into it.

Use the Histogram Allocated Types view to watch for objects that are allocated in the large object heap (those objects larger than 85 KB). You can select specific bar graphs in the left or right pane, and then right-click to see who allocated the memory. This view gives you a high-level view of the objects that are being allocated during the lifetime of your application.

Step 3. Determine Who is Allocating the Memory

On the View menu, click Allocation Graph. Alternatively, you could click in one of the string regions in the graph shown in Figure 1, then right-click and click Show Who Allocated. Clicking this menu item shows specific details about selected allocations rather than all allocations. CLR Profiler displays the graph shown in Figure 2.

Click here for larger image

Figure 2: Allocation graph in CLR Profiler

In this example, you can see that nearly all of the memory is being allocated from the String.Concat method.

The AllocationGraph view enables you to:

  • See the cost of each allocation by method.
  • Analyze allocations that you were not expecting.
  • View possible excessive allocations by a function.
  • Compare different methods of doing the same work.

Step 4. Evaluate What You Can Do to Reduce the Allocations

Now that you know where your application allocates memory, evaluate what you can do to reduce the memory consumption. In this example, one option is to use StringBuilder rather then using string concatenation.

Analyzing Your Application's Allocation Profile

Your application's allocation profile shows you where objects are allocated, the objects' lifetime, and garbage collection behavior. In the following walk-through, the application holds on to objects (and memory) longer than necessary. This walk-through uses the ProfilerSample2.exe sample application that you created earlier.

To analyze your application's allocation profile, follow these steps:

  1. Run CLR Profiler on the sample application.
  2. Identify long-lived objects.
  3. Analyze GC behavior over the lifetime of your application.
  4. Evaluate whether and how to reduce object lifetime.

Step 1. Run CLR Profiler on the Sample Application

Start CLR Profiler and run the ProfilerSample2.exe application.

Step 2. Identify Long-Lived Objects

The sample code allocates 100,000 SolidBrush objects, and some strings, resulting in a total allocation of about 9 MB. Most of this allocation is SolidBrush objects. By selecting the Histogram Reallocated Types view, you can see that about 4 MB of memory is reallocated for SolidBrush objects. This data indicates that SolidBrush objects are surviving garbage collections and are being promoted to higher generations.

To determine the type of objects that are being promoted and the amount of memory these objects use, click Objects by Address on the View menu (see Figure 3).

Click here for larger image

Figure 3: Objects by Address view in CLR Profiler

Note that generations 1 and 2 are mostly composed of SolidBrush objects.

Step 3. Analyze GC Behavior over the Lifetime of Your Application

To view more details, click Time Line on the View menu. Zoom in by setting Vertical Scale to 5 and Horizontal Scale to 1, and then scroll to the right. You should see a window similar to that shown in Figure 4.

Click here for larger image

Figure 4: Time Line view in CLR Profiler

In this figure you can see a "double sawtooth" pattern. The generation 0 collections get rid of strings but retain the brushes (in other words, the brushes survive the collections). After a while, a generation 1 collection cleans up the brushes. The double sawtooth pattern indicates that the generation 0 collections are not able to reclaim all of the memory, and objects are getting promoted which forces a higher generation collection later on.

At this point, you can see that objects are surviving the garbage collections and you need to investigate. A possible area to look at first is the SolidBrush finalizers.

On the main menu of the tool, click Call Tree to open the call tree. To see a list of the finalizers that are called, click through the thread tabs until you find the finalizer thread.

The call tree shows that NATIVE FUNCTION (UNKNOWN ARGUMENTS) has triggered a total of 1,000,234 calls. Because the objects are not cleaned up until the finalizer thread is run, the objects are prevented from being collected and as a result are promoted. Figure 5 shows a sample call tree view window.

Click here for larger image

Figure 5: Call Tree view in CLR Profiler

Step 4. Evaluate Whether and How to Reduce Object Lifetimes

Once you know which objects are long-lived, see if you can reduce their lifetimes. In this case, you simply need to make sure that SolidBrush is disposed of immediately after it is no longer needed, by wrapping it in a using block.

Sample: ProfilerSample1

ProfilerSample1 concatenates strings. The sample code for ProfilerSample1 is as follows.

ProfilerSample1.cs

using System;
public class ProfilerSample1
{
    static void Main (string[] args)
    {
        int start = Environment.TickCount;
        for (int i = 0; i < 1000; i++)
        {
            string s = "";
                for (int j = 0; j < 100; j++)
                {
                    s += "Outer index = ";
                    s += i;
                    s += " Inner index = ";
                    s += j;
                    s += " ";
                }
        }
        Console.WriteLine("Program ran for {0} seconds", 
   0.001*(Environment.TickCount - start));
    }
}

Compiling the Sample

Use the following command line to compile the code.

csc.exe /t:exe ProfilerSample1.cs

Sample: ProfilerSample2

ProfilerSample2 is a simple application that allocates 100,000 SolidBrush objects and some strings. This results in a total allocation of approximately 9 MB. The sample code for ProfilerSample2 is as follows.

ProfilerSample2.cs

using System;
using System.Drawing;

public class ProfilerSample2
{
    static void Main()
    {
        int start = Environment.TickCount;
        for (int i = 0; i < 100*1000; i++)
        {
            Brush b = new SolidBrush(Color.Black);    // Brush has a finalizer
            string s = new string(' ', i % 37);

            // Do something with the brush and the string.
            // For example, draw the string with this brush - omitted...
        }
        Console.WriteLine("Program ran for {0} seconds",
                          0.001*(Environment.TickCount - start));
    }
}

Compiling the Sample

Use the following command line to compile the code.

csc.exe /t:exe ProfilerSample2.cs

Additional Resources

For more information, see the following resources:

For more information about using CLR Profiler, see the following resources:

  • For detailed information about using CLR Profiler to solve common problems related to garbage collection, see "Common Garbage Collection Problems and How They are Reflected In These Views," in CLRProfiler.doc, which is located in the installation folder of the CLRProfiler.exe tool.
  • To learn about the important performance factors of managed code, see MSDN article, "Writing High-Performance Managed Applications: A Primer," at https://msdn.microsoft.com/en-us/library/ms973858.aspx.
  • For information about how the garbage collector works and how to optimize garbage collection, see MSDN article, "Garbage Collector Basics and Performance Hints," at https://msdn.microsoft.com/en-us/library/ms973837.aspx.
  • For information about using CLR Profiler to compare and contrast the performance difference between two ways to code a solution, see the MSDN TV episode, "Profiling Managed Code with the CLR Profiler," at https://www.microsoft.com/resources/msdn/en-us/msdntv/20030729CLRGN.html.

patterns & practices Developer Center

Retired Content

This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

© Microsoft Corporation. All rights reserved.