CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Raymond Lewallen

Framework Design, Agile Coach, President Oklahoma City Developers Group, Microsoft MVP C#, TDD, Continuous Integration, Patterns and Practices, Domain Driven Design, Speaker, VB.Net, C# and Sql Server

Performance Monitoring - Garbage Collection

I see this topic quite a bit on newsgroups and in microsoft chats. People ask how and what to monitor to check out the performance of their application. First and foremost, I tell people to get CLRProfiler.
The CLR Profiler includes a number of very useful views of the allocation profile, including a histogram of allocated types, allocation and call graphs, a time line showing GCs of various generations and the resulting state of the managed heap after those collections, and a call tree showing per-method allocations and assembly loads.
This is an invaluable tool.

This blog post topic isn't about the CLRProfiler though. This is about using PerfMon. When installing .Net, many, many new counters are added that allow you to get detailed and real-time information about the performance of your applications.  We are only going to talk about one set of those for monitoring garbage collections.  Launch PerfMon, and click the + to add new counters. In the Performance Object, select ".Net CLR Memory". It should look something similar to below:

Select a specific application to monitor from the right. You see on my machine the managed applications that are running. I can select OMEA Reader and profile its memory and GC usage.

I always use the following when doing checking the performance of the GC, along with their explanations:

# Gen 0 Collections - This counter displays the number of times the generation 0 objects (youngest; most recently allocated) are garbage collected (Gen 0 GC) since the start of the application. Gen 0 GC occurs when the available memory in generation 0 is not sufficient to satisfy an allocation request. This counter is incremented at the end of a Gen 0 GC. Higher generation GCs include all lower generation GCs. This counter is explicitly incremented when a higher generation (Gen 1 or Gen 2) GC occurs. _Global_ counter value is not accurate and should be ignored. This counter displays the last observed value.

Gen 0 Heap Size - This counter displays the maximum bytes that can be allocated in generation 0 (Gen 0); its does not indicate the current number of bytes allocated in Gen 0. A Gen 0 GC is triggered when the allocations since the last GC exceed this size. The Gen 0 size is tuned by the Garbage Collector and can change during the execution of the application. At the end of a Gen 0 collection the size of the Gen 0 heap is infact 0 bytes; this counter displays the size (in bytes) of allocations that would trigger the next Gen 0 GC. This counter is updated at the end of a GC; its not updated on every allocation.

# Gen 1 Collections - This counter displays the number of times the generation 1 objects are garbage collected since the start of the application. The counter is incremented at the end of a Gen 1 GC. Higher generation GCs include all lower generation GCs. This counter is explicitly incremented when a higher generation (Gen 2) GC occurs. _Global_ counter value is not accurate and should be ignored. This counter displays the last observed value.

Gen 1 heap size - This counter displays the current number of bytes in generation 1 (Gen 1); this counter does not display the maximum size of Gen 1. Objects are not directly allocated in this generation; they are promoted from previous Gen 0 GCs. This counter is updated at the end of a GC; its not updated on every allocation.

# Gen 2 Collections - This counter displays the number of times the generation 2 objects (older) are garbage collected since the start of the application. The counter is incremented at the end of a Gen 2 GC (also called full GC). _Global_ counter value is not accurate and should be ignored. This counter displays the last observed value.

Gen 2 heap size - This counter displays the current number of bytes in generation 2 (Gen 2). Objects are not directly allocated in this generation; they are promoted from Gen 1 during previous Gen 1 GCs. This counter is updated at the end of a GC; its not updated on every allocation.

Large Object Heap Size - This counter displays the current size of the Large Object Heap in bytes. Objects greater than 20 KBytes are treated as large objects by the Garbage Collector and are directly allocated in a special heap; they are not promoted through the generations. This counter is updated at the end of a GC; its not updated on every allocation.

# Bytes in all Heaps - This counter is the sum of four other counters; Gen 0 Heap Size; Gen 1 Heap Size; Gen 2 Heap Size and the Large Object Heap Size. This counter indicates the current memory allocated in bytes on the GC Heaps.

Promoted memory from Gen 0 - This counter displays the bytes of memory that survive garbage collection (GC) and are promoted from generation 0 to generation 1; objects that are promoted just because they are waiting to be finalized are not included in this counter. This counter displays the value observed at the end of the last GC; its not a cumulative counter.

Promoted memorty from Gen 1 - This counter displays the bytes of memory that survive garbage collection (GC) and are promoted from generation 1 to generation 2; objects that are promoted just because they are waiting to be finalized are not included in this counter. This counter displays the value observed at the end of the last GC; its not a cumulative counter. This counter is reset to 0 if the last GC was a Gen 0 GC only.

So I added all of these counters, and opened up OMEA Reader. I started up the PerfMon to start tracking and then did an update for all my webfeeds in OMEA Reader. As soon as that completed, I waited about 5 seconds and then took this snapshot of the report view of my performance monitor:

About a year ago, I spoke with a GC architect and he informed me that a very optimal ratio for generational GC's is 100-10-1 (gen 0-1-2). The app I'm profiling, under the circumstances and with resources available, doesn't reach that optimal level, but I've never profiled anything that does. This GC collection ratio is what you are primarily looking for from these counters. GC's are expensive and you'd like to see them kept at a minimum. You won't see a counter for LOH (large object heap) collections. They happen every time a Gen 2 collection occurs, so the collection counts are the same for both.



Comments

Patrick Steele's .NET Blog said:

# April 6, 2005 8:00 PM

rolo said:

how change garbage collection levels?

where?

# January 25, 2007 4:53 PM

Raymond Lewallen said:

Rolo, the garbage collector promotes objects to other generations automatically.

# January 25, 2007 5:03 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Raymond Lewallen

Working primarily in the public sector during his career, Raymond has designed and built several high profile enterprise level applications for all levels of the government. Raymond now works as a solutions architect for EMC. Raymond is an agile coach, Microsoft MVP C# and also president of the Oklahoma City Developers Group and Oklahoma Agile Developers Group. Raymond spends a lot of his time learning and teaching such things as Test Driven Development, Domain Driven Design, Design Patterns and Extreme Programming practices and principles, to name a few. Raymond is also an advocate of Alt.Net. Raymond is primarily a framework guy, so don't ask him anything about UI :) Check out Devlicio.us!