Managed Memory Model in the .NET Framework
Look up in the sky! It’s a bird! It’s a plane! It’s the CLR Profiler!
The CLR Profiler is an excellent diagnostic tool that monitors an executing managed application and collects data points on object allocation, the managed heap, and garbage collection. The tool is available from Microsoft. If you suspect problems related to the managed heap, the CLR Profiler is an effective tool for diagnosing and pinpointing particular issues. The results of the CLR Profiler are available in a variety of text reports and graphs (mostly histograms). In addition to specific data on the managed heap, the CLR Profiler can provide information on methods in detailed call graphs. Information can be reported during program execution and post mortem. For example, you can obtain a memory summary of the managed heap while the application is executing. Conversely, you can also get a list of objects allocated while the application was running at program completion.
I have great reverence for the CLR Profile as the previous quote would indicate. CLR Profiler is one of the best written .NET applications. The breadth of information and level of detail pertaining to the managed heap and garbage collection is invaluable:
An easy-to-understand summary of the managed heap.
A comprehensive overview of object allocations.
A list of methods that allocate memory on the managed heap, which includes the percentage of allocation attributed to each method.
A variety of call graphs.
Ability to track the lifetime of the Garbage Collector: when garbage collection occurs, the duration between garbage collections, which objects were affected by a particular garbage collection, and more.
A list of finalized objects.
A wide variety of graphs that paint an accurate description of managed memory for nondevelopers, which is helpful for meeting with managers.
Download the current version of the CLR Profiler from the Microsoft downloads Web site: www.microsoft.com/downloads. You can download both the 32- and 64-bit versions of the application. Once installed, the target application can be launched from within the CLR Profiler. The CLR Profiler is intrusive and will adversely affect the performance of the application. For this reason, do not use the product in a production environment.
The CLR Profiler is a complex tool. The following walkthrough provides an introduction to the product. This is not a comprehensive review of the CLR Profiler. Refer to the reference material on the CLR Profiler from Microsoft for additional details.
CLR Profiler Walkthrough
This walkthrough demonstrates the fundamentals of the CLR Profiler. The NoBigPool and BigPool applications are used during the walkthrough. BigPool was described earlier in this chapter. The application maintains a pool of 10 large objects, which are reusable. A large object is defined as an object that resides on the Large Object Heap. The NoBigPool is identical to the BigPool application except it does not maintain a pool of large objects. We assert that BigPool is more efficient because of the pool. In the walkthrough, CLR Profiler will confirm this assertion or force me to rewrite this chapter. We will create 20 big objects. Depending on the application, this will require either releasing or reusing 10 of the big objects. CLR Profiler will allow us to compare the result of the managed heap for both applications.
Each application randomly places secondary large objects, which are increasingly larger, on the Large Object Heap. These secondary objects are occasionally freed. As mentioned previously, during a full garbage collection, the Large Object Heap is swept but not compacted. For this reason, the disparate-sized objects have the potential to slowly fragment the Large Object Heap of both the NoBigPool and BigPool applications. This is being done to simulate a normal pattern of allocation.
Start the CLR Profiler to begin the walkthrough. See 7-3. The Allocations and Calls check boxes should be selected by default. If not, select them to profile the managed heap and function calls, respectively.
Figure 7-3. CLR Profiler window.
We start the walkthrough by profiling the NoBigPool application. Press the Start Application button. Browse to the folder containing bigpool.exe, and select the assembly. The CLR Profiler will start the application, and the NoBigPool user interface will appear momentarily.
The NoBigPool application is shown in 7-4. The Get Large button creates a large object on the Large Object Heap. The Clear Object button sets the reference to a large object to null, which makes the object unreachable and a candidate for future garbage collection. The spin control specifies the big object to clear. Adjust the spin control before pressing the Clear Object button.
Figure 7-4. The user interface for the NoBigPool application.
For the walkthrough, create 10 large objects. Press the Get Large button 10 times. Using the spin control and the Clear Object button, clear the 10 objects. Create another 10 objects. You have now touched 20 big objects in some manner.
Using the CLR Profiler, we can now examine the details of the managed heap for the NoBigPool application. Click the Show Heap Now button in the CLR Profiler to collect current heap information pertaining to the application. The Heap Graph window is displayed. Close the window.
We are more interested in displaying a text summary of the managed heap. From the View menu, select Summary. In the Summary window, find the Garbage Collector Generation Sizes group. This is where the size of the Large Object Heap is displayed. For our example, the size of the Large Object Heap is 4.5 megabytes (MB). See 7-5. This number may vary based on several factors, such as the version of the .NET Framework.
Figure 7-5. The Summary window of the managed heap for the NoBigPool application.
When the managed heap is larger than expected, the CLR Profiler offers a variety of helpful reports to diagnose the problem. For example, you can request list objects and their sizes that have been allocated. You can also view a report that lists the methods where significant allocations are occurring. The list can be sorted by total allocation per method, which is particularly helpful.
From the Summary window, you can display the allocated objects that are currently on the managed heap. In the Heap Statistics group of the Summary window, press the Histogram button next to the Final Heap Bytes value. The Histogram By Size For Surviving Objects window will be displayed. See 7-6.
Figure 7-6. The Histogram By Size For Surviving Objects window.
The Histogram By Size For Surviving Objects window is separated into two panes. The left pane displays a graph of allocated objects—grouped by size. Scroll the pane right to displayed larger objects, such as objects that are on the Large Object Heap. The right pane is both a legend for the left pane and a sequential listing (descending order) of types that are on the managed heap. In our example, System.Byte arrays account for almost 97 percent of the allocated memory, which is worth further investigating. It would be helpful to know where System.Byte arrays are being allocated. That would be an important first step in diagnosing a potential problem. In the right pane, open a context menu (right-click) for the System.Byte array item in the legend. Select Show Who Allocated from the menu. An Allocation Graph is displayed, as shown in 7-7.
Figure 7-7. The Allocation Graph for the CLR Profiler.
Scroll to the right of the Allocation Graph window to view the actual method, or nearest, of the allocation. The graph shows Form1.GetNext as the method where the large object byte array is being allocated. This pinpoints the location of the potential problem, which is helpful. You now know what source code to investigate first.
Let us create 20 objects using the BigPool application and then compare the results with the NoBigPool application. First, close the CLR Profiler and the NoBigPool application. Restart the CLR Profiler. Use the Start Application button to launch the BigPool application from within the CLR Profiler. In the BigPool user interface, press the Get Large button repeatedly to use the 10 objects in the pool. This exhausts the object pool. Clear 10 objects using the spin control and the Clear Object button. Finally, get another 10 objects using the Get Large button. You have now touched 20 big objects.
Let us view the impact of this activity on the Large Object Heap. Press the Show Heap Now button to collect current heap information about the application. Close the Heap Graph window when displayed. Choose View from the menu, and select Summary. The size of the Large Object Heap is reported as 4.2 MB, which is about 8 percent less than the NoBigPool example. See 7-8. This is a significant difference considering the minimum number of objects that were allocated. If that was hundreds of objects, the difference would be substantial.
Figure 7-8. Summary of the managed heap for the BigPool application.