Version 3.5 of the .Net Compact Framework contains a new memory profiler called the CLRProfiler. The CLRProfiler is a great tool for looking into the details of how your application is allocating and using managed objects. For example, the profiler allows you to look at the contents of the GC heap at any point in time, provides a historical record of what's going on in the heap, let's you see which calls in your application are allocating which objects and so on. This level of detail is often needed to diagnose memory-related issues in your device applications. The Compact Framework's version of the CLRProfiler is an adaptation of the profiler that has been available for the full .Net Framework for some time.
In this series of posts I'll walk you through the primary features of the CLRProfiler for the .Net Compact Framework. The profiler contains numerous ways to analyze data about the GC heap so instead of briefly touching on all of them, I'll go into depth on the views I've found most useful.
We'll be looking at the profiler by way of an example, as I've found that learning a new tool is often easier if you have a specific problem to solve rather than just looking at the tool's features without any context. If you'd like more information on a view that I don't cover here you can read the document that ships with the full Framework's version of the profiler.
The sample I'll use to describe the profiler is the beginnings of a game I started to write using the Compact Framework. All the game currently does is allow you to start a new game and to rotate a set of blocks on the screen. Here's a simple view of the application:
The performance problem I'm having with this game involves my drawing logic. The blocks on the screen draw very slowing. On my Dell Axim I can literally see each column of blocks paint individually. Throughout these posts I'll use the CLRProfiler to figure out what I can do to make my game paint more quickly.
Launching an Application with the CLRProfiler
The CLRProfiler ships in the .Net Compact Framework Power Toys package. After installing the Power Toys you can find the CLRProfiler executable (NetCFClrProfiler.exe) in the bin directory of the .Net SDK. On my machine that directory is c:\Program Files\Microsoft.Net\SDK\CompactFramework\v3.5\bin. The Power Toys setup program also adds a menu item for the profiler to the Windows Start menu.
The main window of the profiler is strikingly simple:
Through this main window you can start and stop applications, take snapshots of the GC heap, and control various profiling options. These options include control over whether profiling is currently active and whether allocations, calls or both are logged.
Clicking the "Start Application…" button displays the following form:
On this form you enter the name of the device you'd like to connect to, the fully qualified path to the device executable you'd like to profile, and any command line parameters to be passed to the application. The CLRProfiler supports profiling applications on devices connected either over ActiveSync or TCP/IP. You can also profile applications running on emulators. The profiler supports the same types of devices that the .Net Compact Framework Performance Monitor does(in fact, the device selection and connectivity mechanisms are shared between the two tools).
In my example I have a device connected over ActiveSync and the name of my application is box.exe. Selecting "Connect" from the launch dialog starts the application on device and begins profiling. At this point just run your application as you normally would. The CLRProfiler causes your application to run much slower than normal. In order to speed up the debugging process you can turn profiling on and off for different sections of your application using the "Profiling active" checkbox on the profiler's main form.
When you're done profiling your application, you can stop it either by selecting the "Kill Application" button on the main form of the CLRProfiler or by just closing the application directly on the device.
The Summary Form
After your application exits the CLRProfiler displays the following summary form:
The summary form provides some general statistics about the use of managed memory as your application ran:
- Heap Statistics. The statistics displayed in this group box describe the total size of the objects in the managed heap. The "Allocated Bytes" value counts the total size of allocations made as the application was running. When the Compact Framework garbage collector detects significant fragmentation in the heap, it will compact it. The "Relocated Bytes" value shows how many bytes the garbage collector moved around during compaction. "Final Heap Bytes" shows the size of the managed heap when the application exited and "Objects Finalized" is what you'd expect: the number of objects that had finalizers to run. Note that the Heap Statistics group also provides a count of critical finalizers run. The .Net Compact Framework doesn't have the notion of critical finalization so this value will always be 0.
There are several buttons in this box that launch viewers that allow you to analyze data in various ways. You can view data in histograms based on object size, age, address and so on. I'll describe most of these views in detail in subsequent posts.
- Garbage Collection Statistics. This group box tells you how many garbage collections occurred while your application was being profiled and how many of those collections were induced by calls to GC.Collect. The "Timeline" button enables you to see how the contents of the GC heap changes over time. I think this is one of the coolest views. I'll describe it in great detail in a subsequent post.
- GC Handle Statistics. The GC Handles group box shows you how many handles were created and destroyed while your application ran. Keep in mind that the Compact Framework CLR creates GCHandles under the covers as it executes your application so all of the handles you see here aren't likely to have been created explicitly by you.
- Profiling Statistics. The CLRProfiler enables you to take snapshots of the GC heap as your application is running. I didn't do this while profiling my application, but if I would have I would be able to choose a snapshot from the dropdown and view it.
Getting back to our example, there are several pieces of data on this form that concern me, or are unexpected. The key to analyzing performance data such as this is not just looking at the raw values, but in interpreting the values in the context of what your application was doing as it was being profiled. For example, I profiled the time my application spent painting, yet I see that 5 garbage collections occurred and I created over 6 MB worth of objects. This data leads me to believe that I'm making the garbage collector work harder than it should for my scenario. Clearly there shouldn't be so much activity going on in the managed heap while I'm painting boxes on the screen!
In my next post, we'll use some of the histogram views to see how many objects my application is creating and of what type they are.
This posting is provided "AS IS" with no warranties, and confers no rights.