<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Rico Mariani's Performance Tidbits : using tools</title><link>http://blogs.msdn.com/ricom/archive/tags/using+tools/default.aspx</link><description>Tags: using tools</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Introducing Performance Console</title><link>http://blogs.msdn.com/ricom/archive/2006/08/03/introducing-performance-console.aspx</link><pubDate>Fri, 04 Aug 2006 02:03:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:688019</guid><dc:creator>ricom</dc:creator><slash:comments>7</slash:comments><comments>http://blogs.msdn.com/ricom/comments/688019.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ricom/commentrss.aspx?PostID=688019</wfw:commentRss><description>&lt;P&gt;Ever wonder how I get those nice looking HTML call trees with attributed costs like &lt;A href="http://blogs.msdn.com/ricom/archive/2006/07/18/670314.aspx"&gt;this one here&lt;/A&gt;&amp;nbsp;in my last quiz?&lt;/P&gt;
&lt;P&gt;Well, it turns out that &lt;A href="http://blogs.msdn.com/joshwil"&gt;Josh Williams&lt;/A&gt;&amp;nbsp;wrote an excellent program for massaging the perf results that come out of our profiler.&amp;nbsp; The way it works is that you get a profile as usual and then you use the tools (or the UI) to export the data in .csv format.&amp;nbsp; Josh shows how in &lt;A href="http://blogs.msdn.com/joshwil/archive/2006/08/03/687783.aspx"&gt;this article&lt;/A&gt;&amp;nbsp;from the command line but the export function does the same job.&lt;/P&gt;
&lt;P&gt;Once you have your .csv files&amp;nbsp;you can use the newly released &lt;A href="http://www.microsoft.com/downloads/details.aspx?FamilyId=5A04EE30-6259-4FAD-BF05-CCB72BE09B4A&amp;amp;displaylang=en"&gt;Performance Console&lt;/A&gt; to read those files and browse them.&amp;nbsp; Now I love this tool because it includes some of my favorite features (yes I nag very well, Josh can attest *grin*) such as "show me the call tree with all functions at least&amp;nbsp;5% inclusive time." And the best part is you don't have to keep clicking open-open-open to browse deep call trees.&lt;/P&gt;
&lt;P&gt;By the way, our own profiler team looks at this tool and others very closely in deciding what to do in their next iteration so hopefully we'll see more and more of this stuff being available without special downloads.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;But why wait?&amp;nbsp; You can have this goodness now.&lt;/P&gt;
&lt;P&gt;One last great thing about the perfconsole tool -- you can save the results of a session as HTML and then mail them or blog them or whatever.&lt;/P&gt;
&lt;P&gt;Josh will be posting some usage hints soon.&amp;nbsp; But for now, go ahead and grab it and play.&amp;nbsp; You'll love it!&amp;nbsp; Heck, post me some of your favorite outputs.&lt;/P&gt;
&lt;P&gt;Did I mention its extensible?&amp;nbsp; I hope he posted the whole manual or I have to nag some more. :)&lt;/P&gt;
&lt;P&gt;Try it, you won't be sorry.&lt;/P&gt;
&lt;P&gt;UPDATE:&amp;nbsp; Josh just wrote an introduction to Performance Console, find it&amp;nbsp;&lt;A href="http://blogs.msdn.com/joshwil/archive/2006/08/04/688599.aspx"&gt;here&lt;/A&gt;.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=688019" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ricom/archive/tags/performance/default.aspx">performance</category><category domain="http://blogs.msdn.com/ricom/archive/tags/using+tools/default.aspx">using tools</category></item><item><title>LogDump:  CLRProfiler Log analysis tool</title><link>http://blogs.msdn.com/ricom/archive/2005/08/08/449246.aspx</link><pubDate>Tue, 09 Aug 2005 03:44:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:449246</guid><dc:creator>ricom</dc:creator><slash:comments>5</slash:comments><comments>http://blogs.msdn.com/ricom/comments/449246.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ricom/commentrss.aspx?PostID=449246</wfw:commentRss><description>&lt;P&gt;Ever wonder how I produce nice textual allocation summaries like this one?&lt;/P&gt;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;This report shows allocations in dictread.log&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;Total Allocations 136301 Objects 6694232 Bytes&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;Top 10 Allocated Types&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;&amp;nbsp;&amp;nbsp; Count&amp;nbsp;&amp;nbsp;&amp;nbsp; Bytes Type&lt;BR&gt;&amp;nbsp; 108040&amp;nbsp; 5836392 System.String&lt;BR&gt;&amp;nbsp;&amp;nbsp; 26726&amp;nbsp;&amp;nbsp; 534520 DictionaryEntry&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 73&amp;nbsp;&amp;nbsp; 275604 System.Object []&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1010&amp;nbsp;&amp;nbsp;&amp;nbsp; 20200 System.Text.StringBuilder&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 51&amp;nbsp;&amp;nbsp;&amp;nbsp; 10466 System.Char []&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 11&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 6358 System.Byte []&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 53&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1272 System.Collections.ArrayList&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 52&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1248 ArrayListEnumeratorSimple&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 6&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1008 bucket []&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 18&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 504 System.Security.SecurityElement&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;Top 10 Allocating Stacks&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;Stack 1 allocates 3132656 bytes&lt;BR&gt;NS.Test::Main static void (String[])&lt;BR&gt;Dictionary::.ctor void ()&lt;BR&gt;DictionaryEntry::Parse static DictionaryEntry (String)&lt;BR&gt;System.String::Substring String (int32 int32)&lt;BR&gt;&amp;nbsp;&amp;nbsp; 80178&amp;nbsp; 3132656 System.String&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;Stack 2 allocates 2402768 bytes&lt;BR&gt;NS.Test::Main static void (String[])&lt;BR&gt;Dictionary::.ctor void ()&lt;BR&gt;System.IO.StreamReader::ReadLine String ()&lt;BR&gt;&amp;nbsp;&amp;nbsp; 25723&amp;nbsp; 2382668 System.String&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1005&amp;nbsp;&amp;nbsp;&amp;nbsp; 20100 System.Text.StringBuilder&lt;/FONT&gt;&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=2&gt;Stack 3 allocates 534520 bytes&lt;BR&gt;NS.Test::Main static void (String[])&lt;BR&gt;Dictionary::.ctor void ()&lt;BR&gt;DictionaryEntry::Parse static DictionaryEntry (String)&lt;BR&gt;&amp;nbsp;&amp;nbsp; 26726&amp;nbsp;&amp;nbsp; 534520 DictionaryEntry&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;
&lt;P&gt;
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;That's a report similar to the one that I generated for &lt;a href="http://blogs.msdn.com/ricom/archive/2005/05/20/420614.aspx"&gt;Performance Quiz #6&lt;/A&gt;&amp;nbsp;-- I get a ton of mileage out of these.&amp;nbsp; Just use &lt;A href="http://download.microsoft.com/download/1/2/e/12ed42f8-1c9c-4f97-a969-581a61cc588c/clrprofilerb2.exe"&gt;CLRProfiler for Beta2 &lt;/A&gt;or greater, save your .log files and run &lt;a href="http://blogs.msdn.com/ricom/articles/449244.aspx"&gt;this little tool &lt;/A&gt;over it to get a summary of the total costs and their major sources.&amp;nbsp; This little guy saves me a ton of time and I've used it to anlayze multi-gigabyte log files where the UI approach just isn't going to work.&amp;nbsp; The algorithm is to just attribute each allocation to a callstack and then compute the callstacks with the biggest cost -- which is basically the same as going into the tree view in the UI and opening the biggest choice at each step, then 2nd biggest etc.&lt;/P&gt;
&lt;P&gt;I've just posted &lt;a href="http://blogs.msdn.com/ricom/articles/449244.aspx"&gt;the source code for this little dumper&lt;/A&gt; -- it's a quickie I wrote one afternoon and it shows -- but hopefully you can get some good usage out of it as well.&lt;/P&gt;
&lt;P&gt;Legal Note: Like all other code samples this program is offered&amp;nbsp;&lt;EM&gt;AS&amp;nbsp;IS&amp;nbsp;&lt;/EM&gt;with &lt;EM&gt;no warranty implied &lt;/EM&gt;and &lt;EM&gt;confers no rights&lt;/EM&gt;&lt;STRONG&gt;.&lt;/STRONG&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=449246" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ricom/archive/tags/performance/default.aspx">performance</category><category domain="http://blogs.msdn.com/ricom/archive/tags/using+tools/default.aspx">using tools</category></item><item><title>Narrowing Down Performance Problems in Managed Code</title><link>http://blogs.msdn.com/ricom/archive/2005/05/25/421926.aspx</link><pubDate>Thu, 26 May 2005 03:10:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:421926</guid><dc:creator>ricom</dc:creator><slash:comments>16</slash:comments><comments>http://blogs.msdn.com/ricom/comments/421926.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ricom/commentrss.aspx?PostID=421926</wfw:commentRss><description>&lt;P&gt;My &lt;a href="http://blogs.msdn.com/ricom/archive/2005/05/23/421205.aspx"&gt;last entry&lt;/A&gt; was some generic advice about how to do a good performance investigation.&amp;nbsp; I think actually it's too generic to be really useful -- in fact I think it fails&amp;nbsp;my Peanut Butter Sandwich Test.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Digression to discuss the Peanut Butter Sandwich Test&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;I review a lot of documents and sometimes they say things that are so obvious as to be uninteresting.&amp;nbsp; The little quip I have for this situation is, "Yes what you are saying is true of [the system] but it's also true of peanut butter sandwiches." Consider a snippet like this one, "Use a cache where it provides benefits," and compare with, "Use a peanut butter sandwich where it provides benefits."&amp;nbsp;&amp;nbsp;Both seem to work... that's a bad sign.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;You certainly don't want to get an F on the Peanut Butter Sandwich Test but hopefully you won't settle for just a C-.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Back on topic&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;I thought it would be good to follow up the generic advice with some specific suggestions for things to look at. These are things I look at in step 2 or 3 of the investigation.&lt;/P&gt;
&lt;P&gt;Under .NET CLR Memory, check "% Time in GC" if it's getting near 10% or higher you may have some memory issues, consider these secondary tests:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;is the raw allocation rate "Allocated Bytes/sec" too high? -&amp;gt; reduce total allocations 
&lt;LI&gt;is the promotion rate "Promoted Memory from Gen 1" too high? -&amp;gt; be careful about object lifetimes, avoid &lt;a href="http://blogs.msdn.com/ricom/archive/2003/12/04/41281.aspx"&gt;"mid-life crisis"&lt;/A&gt; 
&lt;LI&gt;is the finalization rate "Finalization Survivors" too high? -&amp;gt; make sure you are disposing the key objects 
&lt;LI&gt;is the heap growing when it shouldn't "# Bytes in all Heaps" -&amp;gt; check for reference leaks &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Is the CPU not saturated when it should be?&amp;nbsp; Look under .NET CLR LocksAndThreads &lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;is the "Contention Rate / sec" counter high compared to your throughput rate? -&amp;gt; you should re-examine your locking strategy 
&lt;LI&gt;is the "# of current physical Threads" too low for the problem?&amp;nbsp; -&amp;gt; (ammended) more parallelism may be helpful, consider using the ThreadPool if not already in use, possibly adjust ThreadPool parameters to get more threads (not usually needed) 
&lt;LI&gt;in the "Thread" category examine "Context Switches / sec", is this high compared to your throughput rate?&amp;nbsp; -&amp;gt; perhaps the workitem you are giving threads in the thread pool is too small, consider something chunkier&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Is the throughput rate low even though the CPU is saturated?&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;look under ".NET CLR Exceptions", is "# of Excepts Thrown / sec" high compared to your throughput? -&amp;gt; consider reducing use of exceptions in common paths 
&lt;LI&gt;look under ".NET CLR Interop", is "# of marshalling" growing too fast?&amp;nbsp; -&amp;gt; consider simplifying the arguments passed in interop cases so that marshalling is cheaper 
&lt;LI&gt;look under ".NET CLR Security", is "% Time in RT checks" significant?&amp;nbsp; -&amp;gt; consider simplying the demands being placed on the security system to lower the cost of security checks 
&lt;LI&gt;look under ".NET CLR Jit", is "% Time in Jit" significant? This counter shouldn't stay high because jitting should settle out, if it remains high then perhaps there is dynamic code generation via reflection going on -&amp;gt; simply dynamic code cases&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;This just a taste of course, and each of these items would likely lead to further investigation with a profiling tool that is suitable to drilling into that particular kind of problem but these are examples of leading indicators that I use.&lt;/P&gt;
&lt;P&gt;For more information on the GC Performance counters specifically see &lt;a href="http://blogs.msdn.com/maoni"&gt;Maoni's&lt;/A&gt; blog entry &lt;a href="http://blogs.msdn.com/maoni/archive/2004/06/03/148029.aspx"&gt;on that subject&lt;/A&gt;.&amp;nbsp; Her most recent article is on &lt;a href="http://blogs.msdn.com/maoni/archive/2005/05/06/415296.aspx"&gt;using the GC efficiently&lt;/A&gt; also very interesting, lots of good details there.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=421926" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ricom/archive/tags/performance/default.aspx">performance</category><category domain="http://blogs.msdn.com/ricom/archive/tags/using+tools/default.aspx">using tools</category></item><item><title>How To Do A Good Performance Investigation</title><link>http://blogs.msdn.com/ricom/archive/2005/05/23/421205.aspx</link><pubDate>Tue, 24 May 2005 02:54:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:421205</guid><dc:creator>ricom</dc:creator><slash:comments>11</slash:comments><comments>http://blogs.msdn.com/ricom/comments/421205.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ricom/commentrss.aspx?PostID=421205</wfw:commentRss><description>&lt;P&gt;I find that sometimes people have difficultly just getting started when doing a performance analysis – meaning they’re faced with a potentially big problem and don’t know where to begin.&amp;nbsp; Over the years many people have come to me under those circumstances and asked me how I would approach the problem.&amp;nbsp; So today I’m trying to distill those bits of advice – my&amp;nbsp;&lt;EM&gt;Modus Operandi &lt;/EM&gt;– into some simple steps in the hope that it might help others to get off to a good start.&lt;/P&gt;
&lt;P&gt;So here it is, Rico’s advice on how to do a good performance investigation.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Preliminaries&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The first thing to remember is not to try to do this in a rushed way.&amp;nbsp; The more of a hurry you are in to get to the bottom of the problem the less you can afford to be hasty.&amp;nbsp; Be deliberate and careful.&amp;nbsp; Block out a good chunk of time to think about the problem.&amp;nbsp; Make sure you have the resources you need to succeed – that means enough access to the right hardware and the right people.&amp;nbsp; Prepare a log book – electronic if you like – so that you can keep notes and interim results at each step.&amp;nbsp; This will be the basis of your final report and it will be an invaluable reference to anyone that follows in your footsteps – even if (especially if?) that someone is you.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Step 1 – Get the Lay of the Land&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Before you look at any code, talk to the people involved.&amp;nbsp; You’ll want to get a feel for what they are trying to accomplish.&amp;nbsp; What are their key difficulties?&amp;nbsp; What is inherently hard about this problem?&amp;nbsp; What is the overall organization of the system?&amp;nbsp; What are the inherent limitations of those choices?&amp;nbsp; What is the chief complaint with the current system?&amp;nbsp; What would a successful system look like?&amp;nbsp; How do they measure success? &lt;/P&gt;
&lt;P&gt;In addition to a basic understanding of how the system is intended to work the key question you want to answer is this:&amp;nbsp; Which resource is the one that should be critical in this system?&amp;nbsp; Is the problem fundamentally CPU bound, disk bound, network bound?&amp;nbsp; If things were working perfectly what would be constraining the performance?&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Step 2 – Identify the current bottlenecks&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;In this step we cast a broad net to see what resource is the current limiting factor.&amp;nbsp; The tool I reach for first is PerfMon.&amp;nbsp; Look at key counters like CPU Usage, Memory Usage, Disk and Network I/O.&amp;nbsp; Examine these on all the various different machines involved if this is a server problem (i.e. check all the tiers).&amp;nbsp; Check for high levels of lock contention.&lt;/P&gt;
&lt;P&gt;At this point you should be able to identify which resource is currently the one that is limiting performance.&amp;nbsp; Often it is not the resource identified in Step 1.&amp;nbsp; &lt;/P&gt;
&lt;P&gt;If it is the correct resource – the one that is supposed to be the limiting factor in this kind of computing – that’s a good preliminary sign that sensible algorithms have been selected.&amp;nbsp; If it’s the wrong resource it means you are going to be looking for design problems where a supposedly non-critical resource has been overused to the point that it has become the critical resource.&amp;nbsp;&amp;nbsp; The design will have to be altered such that it does not have this (fundamentally unnecessary) dependence.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Step 3 – Drill down on the current bottleneck&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;A common mistake in a performance analysis is to try to do step 3 before step 2.&amp;nbsp; This is going to lead to a good bit of waste because you could do a deep analysis of say CPU usage when CPU usage isn’t the problem.&amp;nbsp; Instead, choose tools that are good at measuring the problem resource and don’t worry so much about the others for now.&amp;nbsp; If it’s hard to measure the resource in question, add instrumentation for this resource if possible.&amp;nbsp; The goal is to find out as much as you can about what is causing the (over) use of the bottleneck resource.&lt;/P&gt;
&lt;P&gt;When doing this analysis, consider factors that control the resources at different system levels starting from the largest and going to the smallest.&amp;nbsp; Is there something about the overall system architecture that is causing overuse of this resource?&amp;nbsp; Is it something in the overall application design?&amp;nbsp; Or is it a local problem with a module or subcomponent?&amp;nbsp;&amp;nbsp; Most significant problems are larger in scope, look at those first.&amp;nbsp; They are the easiest to diagnose and sometimes the easiest to correct.&amp;nbsp; E.g. if caching were disabled on a web server you could expect big problems in the back end servers.&amp;nbsp; You’ll want to make sure caching is working properly before you decide to (e.g.) add more indexes to make some query faster.&lt;/P&gt;
&lt;P&gt;Your approach will need to be tailored to the resource and the system. For CPU problems a good time profiler can be invaluable.&amp;nbsp; For SQL problems there’s of course SQL Profiler (find the key queries) and Query Analyzer (view the plans).&amp;nbsp; For memory issues there are abundant performance counters that can be helpful, including the raw memory counters and .NET memory management ones, and others.&amp;nbsp; Tracking virtual memory use over time can be helpful – sampling with vadump is handy.&amp;nbsp; Examination of key resources by breaking into the system with a debugger can also be useful.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Step 4 – Identify anomalies in the measurements&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The most interesting performance problems are almost always highlighted by anomalous observed costs.&amp;nbsp; Things are happening that shouldn’t be happening or need not happen.&amp;nbsp; If the critical resource isn’t the “correct” one as identified in Step 1 it’s almost certainly the case that your root-cause analysis will find an undesired use of the resource.&amp;nbsp; If the resource was the “correct” one then you’ll be looking for overuse to get an improvement.&lt;/P&gt;
&lt;P&gt;In both cases it is almost always helpful to look at the resource costs per unit-of-work.&amp;nbsp; What is the “transaction” in this system?&amp;nbsp; Is it a mouse click event?&amp;nbsp; Is it a business transaction of some kind?&amp;nbsp; Is it an HTML page delivered to the user?&amp;nbsp; A database query performed?&amp;nbsp; Whatever it is look at your critical resource and consider the cost per unit of work.&amp;nbsp; E.g. consider CPU cycles per transaction, network bytes per transaction, disk i/o’s per transaction, etc.&amp;nbsp;&amp;nbsp; These metrics will often show you the source of the mistake – consider in my recent analysis of Performance Quiz #6 where I looked at the number of string allocations per line of input and then bytes allocate per byte in the input file.&amp;nbsp; Those calculations were both easy and revealing.&lt;/P&gt;
&lt;P&gt;Expressing your costs in a per-unit-of work fashion will help you to see which costs are reasonable and which are problems.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Step 5 – Create a hypothesis and verify it&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Based on the analysis in step 4 you should be able to find a root cause for the current bottleneck.&amp;nbsp; Design an experiment to validate that this is the case.&amp;nbsp; This can be a very simple test such as “if we change the settings [like so], it should make the problem much worse.”&amp;nbsp; Take advantage of any kind of fairly quick validation that you can do… the worst thing to do is to plunge into some massive correction without being sure that you’ve really hit the problem.&amp;nbsp; If there is nothing obvious, consider doing only a small fraction of the corrective work.&amp;nbsp; Perhaps just enough for one test case to function – validate that before you proceed.&lt;/P&gt;
&lt;P&gt;The trick to doing great performance work is to be able to try various things and yet spend comparatively little time on the losing strategies while quickly finding and acting on the winners.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Step 6 – Apply the finished corrections, verify, and repeat as needed&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;After Step 5 you should be highly confident that you have a winning change on your hands.&amp;nbsp; Go ahead and finish it up to production quality and apply those changes.&amp;nbsp; Make sure things went as you expected and only then move on.&amp;nbsp; If your changes were not too sweeping you can probably resume at Step 2, or maybe even Step 3.&amp;nbsp; If they were very big changes you might have to go back to Step 1.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Step 7 – Write a brief report&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Summarize your method and findings for your teammates.&amp;nbsp; It’s invaluable as a learning exercise for yourself and as a long-term resource for your team.&amp;nbsp; &lt;BR&gt;&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Post Script&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;I wrote a followup article which offers more prescriptive advice about managed code specifically -- see &lt;A id=_ctl0__ctl0__ctl0__ctl0_RecentPosts__ctl0_postlist__ctl0_EntryItems__ctl1_PostTitle HREF="/ricom/archive/2005/05/25/421926.aspx"&gt;Narrowing Down Performance Problems in Managed Code&lt;/A&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=421205" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ricom/archive/tags/performance/default.aspx">performance</category><category domain="http://blogs.msdn.com/ricom/archive/tags/design+advice/default.aspx">design advice</category><category domain="http://blogs.msdn.com/ricom/archive/tags/using+tools/default.aspx">using tools</category></item><item><title>Performance Quiz #6 -- Analyzing the managed code</title><link>http://blogs.msdn.com/ricom/archive/2005/05/17/performance-quiz-6-analyzing-the-managed-code.aspx</link><pubDate>Wed, 18 May 2005 00:12:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:418863</guid><dc:creator>ricom</dc:creator><slash:comments>14</slash:comments><comments>http://blogs.msdn.com/ricom/comments/418863.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ricom/commentrss.aspx?PostID=418863</wfw:commentRss><description>&lt;P&gt;Raymond didn't post an update today so I think this is a good time for me to post the first analysis of the managed code. There's some things to notice right away and I've highlighted them in the table below. This table was generated in the same way as the others and again I've pruned the tree so that no functions with inclusive time less than 5% are shown, so that means some things won't add up because some small things are gone -- still it helps focus attention on the relevant bits and keeps this table reasonably sized. I've also removed functions which were basically just zero or near zero cost wrappers, so there are some internal "..." entries. That's what those are.&lt;/P&gt;
&lt;TABLE&gt;

&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&lt;STRONG&gt;Name &lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD align=right&gt;
&lt;P&gt;&lt;FONT size=2&gt;&lt;STRONG&gt;&amp;nbsp;Exclusive&lt;BR&gt;&lt;/STRONG&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;STRONG&gt;&amp;nbsp;Percent&lt;/STRONG&gt; &lt;/FONT&gt;&lt;/P&gt;&lt;/TD&gt;
&lt;TD align=right&gt;&lt;FONT size=2&gt;&lt;STRONG&gt;&amp;nbsp;Inclusive&lt;BR&gt;Percent&lt;/STRONG&gt; &lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&lt;STRONG&gt;&amp;nbsp; __CorExeMain &lt;/STRONG&gt;(from mscoree.dll)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;92.31&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; __CorExeMain (from mscorwks.dll)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;88.46&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ExecuteEXE(struct HINSTANCE__ *)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;69.23&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SystemDomain::ExecuteMainMethod(...)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;69.23&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Assembly::ExecuteMainMethod(class PtrArray * *)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;59.62&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; …&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;59.62&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;STRONG&gt;NS.Test.Main(string[])&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;59.62&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; NS.Test.Dictionary..ctor()&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;1.92&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;58.65&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.IO.StreamReader.ReadLine()&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;3.85&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;28.85&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.IO.StreamReader.ReadBuffer()&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;16.35&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Text.DecoderNLS.GetChars(...)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;15.39&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; …&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;15.39&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; …&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;15.39&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;STRONG&gt;System.Text.DBCSCodePageEncoding.GetChars(...)&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;15.39&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;15.39&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.String.CtorCharArrayStartLength(char[],int32,int32)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;5.77&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;STRONG&gt;NS.Test.DictionaryEntry.Parse(string)&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;2.89&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;26.92&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.String.Substring(int32,int32)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.96&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;14.42&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.String.InternalSubString(int32,int32)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;13.46&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;7.69&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; …&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;6.73&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Alloc(unsigned long,int,int)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;6.73&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;6.73&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String::IndexOfChar(...)&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;1.92&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;5.77&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;STRONG&gt;EnsureEEStarted(...)&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;18.27&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;18.27&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ...&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;FONT size=2&gt;18.27&lt;/FONT&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;STRONG&gt;InitializeProfiling&lt;/STRONG&gt;&lt;/FONT&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;0.00&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;
&lt;TD class=xl22 align=right&gt;&lt;STRONG&gt;&lt;FONT size=2&gt;6.73&lt;/FONT&gt;&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TABLE&gt;
&lt;P&gt;First thing to notice is that the time attributed to the overall "main" of the program (that would be &lt;EM&gt;__CorExeMain&lt;/EM&gt;) is only 92%. Why isn't that 100%? Well there's a few reasons. The first is that the CLR has more than one thread so some of the time went to spinning up the finalizer thread for instance. A second reason is that some percentage of the run will be kernal time and we won't be able to attribute those stacks to anything so those are gone. And the third, and most subtle reason, is that some places during the run of a managed program have a stack frame that is so crazy (usually because we're in the middle of an exotic transition) that the profiler can't walk it. That causes it to drop some samples. The result of these factors is that main is usually close to but not 100% of the time. In this case 8% of the samples were elsewhere or not traceable.&lt;/P&gt;
&lt;P&gt;But now lets get to some meatier conclusions: &lt;EM&gt;NS.Test.Main()&lt;/EM&gt; which is the managed main function is only getting 59% of the time. Even allowing for the the fact that main was only 92.31% of the time, the managed main is only 64.6% of that. Why is that? &lt;/P&gt;
&lt;P&gt;Well there were some assorted small things that were in the set of things pruned from this view (at less than 5%) but they added up... some of it was checking the security configuration only to discover that we are full trust, some of it was looking for possible administrative configurations, some of it was setting up the performance counters for this instance of the application.&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Another chunk of the time still visible even in this pruned view -- &lt;EM&gt;EnsureEEStarted()&lt;/EM&gt; for instance at 18.27% of the startup including 6.73% to get the profiling services working. That part of the cost would naturally not even be present if the program wasn't run under the profiler.&amp;nbsp; All these startup tasks tend to require other DLLs, typically already loaded but we still have to get access to them in this process. It all adds up. &lt;/P&gt;
&lt;P&gt;Am I happy with a number like 59.62%? Well no, it's my job to make that number bigger (i.e. reduce the overhead) but this is a fairly small application... the whole run will be just over 100ms so basically the (fairly) fixed cost of starting up the runtime is significant in this case. It would be less daunting if the program did more. &lt;/P&gt;
&lt;P&gt;Note to reviewers of managed systems: If you want to make a framework -- any framework -- look bad, simply ask it to do nothing. It turns out pretty much all known frameworks are lousy at doing nothing. So "hello world" for instance is a great way to make a framework look like a pig.&lt;/P&gt;
&lt;P&gt;Still there's room for improvement there which is why I have a job :)&lt;/P&gt;
&lt;P&gt;Moving on, &lt;EM&gt;System.IO.StreamReader.ReadLine() &lt;/EM&gt;is at 28% which may not look too high, after all this program is primarily a parser. However, looking closer we can see that the hefty work, decoding code page 950, is in &lt;EM&gt;DBCSCodePageEncoding.GetChars()&lt;/EM&gt; and that's only at 15.39%. It's not unhealthy to see a number like 15% spent doing one of the nice meaty jobs at hand but how did we go from 15% to decode the text all the way to 28% to decode the text and break it into lines? That seems excessive... maybe we could do something about that.&lt;/P&gt;
&lt;P&gt;Looking further down &lt;EM&gt;NS.Test.DictionaryEntry.Parse()&lt;/EM&gt; is up at 26% compared to the 28% for &lt;EM&gt;System.IO.StreamReader.ReadLine().&lt;/EM&gt; Now what's up there... it's just some simple scanning and string allocation. The string allocation should be the main cost yet it's only 14.42% of the time in &lt;EM&gt;Substring &lt;/EM&gt;of which a mere 6.73%. Plus the &lt;EM&gt;IndexOfChar &lt;/EM&gt;worker, which should be doing most of the searching, is only at 5.77%. Well there is a good bit of control flow and function call setup in the &lt;EM&gt;Parse &lt;/EM&gt;function. That's probably a good chunkof it right there.&lt;/P&gt;
&lt;P&gt;Still, the overall picture is pretty good. No exotic critical sections, no weird interlocked operations, very little time in the allocator. You can see why we posted a great out-of-the-gate number like 124ms. Even with the CLR's fixed overhead we got great value out of the allocator and the input mapping services.&lt;/P&gt;
&lt;P&gt;But we can make it better still :)&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=418863" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ricom/archive/tags/performance/default.aspx">performance</category><category domain="http://blogs.msdn.com/ricom/archive/tags/using+tools/default.aspx">using tools</category><category domain="http://blogs.msdn.com/ricom/archive/tags/quiz/default.aspx">quiz</category></item><item><title>Three techniques for tracking down memory leaks due to undisposed objects</title><link>http://blogs.msdn.com/ricom/archive/2005/04/15/408650.aspx</link><pubDate>Fri, 15 Apr 2005 21:37:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:408650</guid><dc:creator>ricom</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/ricom/comments/408650.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ricom/commentrss.aspx?PostID=408650</wfw:commentRss><description>&lt;P&gt;People often ask me for tips/tricks on how to find out which objects are not being properly disposed.&amp;nbsp;&amp;nbsp;But before I go any further I should talk briefly about why that's a problem.&lt;/P&gt;
&lt;P&gt;If an object is Finalizable that means that the runtime must run the Finalize method on that object (in C# you use the ~Class syntax to define the finalizer) before the memory can be reclaimed.&amp;nbsp; The mechanism for doing this is fairly straightforward -- whenever an object that is finalizable first becomes unreachable it is put on a queue of objects needing finalization.&amp;nbsp; Naturally we only discover that the object is unreachable during a garbage collection, and since we need to keep the object alive to finalize it the object must survive that collection.&amp;nbsp; When the finalizer runs the object will be again unreachable and on the next collection the memory will be recovered as usual.&amp;nbsp; See &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/dotnetGCbasics.asp"&gt;Garbage Collector Basics and Performance Hints&lt;/A&gt;&amp;nbsp;and &lt;a href="http://blogs.msdn.com/ricom/archive/2003/12/02/40780.aspx"&gt;Two Things to Avoid for Better Memory Usage&lt;/A&gt; for more details.&lt;/P&gt;
&lt;P&gt;Now the problem with all this is that it means every Finalizable object must survive at least one collection and so they are more likely to be promoted into older generations where getting rid of them is more expensive.&amp;nbsp; Furthermore, collections of those older generations is less frequent so the memory for the finalizable object may not be reclaimed as quickly as we might like even if the finalizer is run comparatively soon.&lt;/P&gt;
&lt;P&gt;To avoid these problems, every finalizable object should also be IDisposable (and most are).&amp;nbsp; If an object is IDisposable it means that when you are done with it you can call the Dispose() method which releases any unmanaged resources associated with the object and tells the runtime that the object no longer needs to be finalized.&amp;nbsp; As a consequence the resources are returned to the system as quickly as possible and the object doesn't need to survive for finalization -- it dies nice and quick like a regular object.&lt;/P&gt;
&lt;P&gt;With that background I can get back on topic:&amp;nbsp; Sometimes despite our best efforts we forget to call the Dispose method on every Disposable object.&amp;nbsp; When that happens those objects go through finalization.&amp;nbsp; If that happens a lot we can get &lt;a href="http://blogs.msdn.com/ricom/archive/2003/12/04/41281.aspx"&gt;Mid-Life Crisis&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;So, how do we detect that this is happening?&amp;nbsp; And if it is happening how do we track down the source of the leak?&amp;nbsp; I have some approaches for now and some hints as to how we'll do this in the future.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Technique #1&amp;nbsp;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;If you want to know if finalization is a problem any regular profiler can give you a good hint.&amp;nbsp; Using CLR Profiler for instance you can run your application then use the Call Tree view and select the various tabs until you find the one for the finalizer thread.&amp;nbsp; It's very obvious which one that is because all the method names will be things like "SomeClass::Finalize".&amp;nbsp; A lot of times just knowing which classes are being finalized a lot is enough to fix the problem.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Technique #2&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;If you see a lot of finalization but the finalizer is associated with a class or classes for which there are many instances and you want to know which instance is the bad one there is more you can do.&amp;nbsp; If the class is one you control you can call store a stacktrace in the object when it is created with "StackTrace(Thread targetThread, bool needFileInfo)"&amp;nbsp; (note this is a heavyweight thing to do, not something you want to ship with) then when the object is finalized you can dump that stack trace so that you will see what the stacktrace created the object and you can target that trace for elimination.&amp;nbsp; To keep the cost down you might take a stacktrace at random only 1/1000th of the time and you could still get enough samples of the bad pattern to identify it over a long run.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Technique #3&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;If you don't own the&amp;nbsp;class then its harder but you still might be able to make some headway.&amp;nbsp; The first thing you need is a durable way to identify a particular instance of a particular class -- you can't use an object pointer for this because the object can move over time but for many classes (almost all reference types) you can hash the class using GetHashCode() and the hash doesn't change over the life of the object.&amp;nbsp;&amp;nbsp;Hash the objects you care about as soon as you can and capture the best callstack you can to&amp;nbsp;finger their creator (see above).&amp;nbsp;&amp;nbsp;Next use your favorite debugger to stop in the Finalizer for the classes in question -- once you are stopped there dump the object's&amp;nbsp;hash code in the debugger&amp;nbsp;-- you should be able to put a call to GetHashCode() in the debugger watch window for instance.&amp;nbsp; You can use that hash code to find the callstack that created the object.&amp;nbsp; If the callstack is uninteresting or too expensive you can use any other friendly description of the callsite that will help you diagnose the originating location.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;Looking forward:&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;The profiling API (the one that CLR Profiler uses for instance) in V1.0 and V1.1 provides almost enough information to do this job more automatically.&amp;nbsp; In V2.0 (Whidbey) we're doing more:&lt;/P&gt;
&lt;P&gt;ICorProfilerCallback2 adds (among other things) this little gem:&lt;/P&gt;
&lt;P&gt;&lt;FONT face="Courier New" size=1&gt;&lt;FONT size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; virtual HRESULT STDMETHODCALLTYPE FinalizeableObjectQueued( &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* [in] */ DWORD finalizerFlags,&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; /* [in] */ ObjectID objectID) = 0;&lt;/FONT&gt;&lt;BR&gt;&lt;/FONT&gt;&lt;BR&gt;This fellow tells profilers directly when an object is queued for finalization.&amp;nbsp; The object allocation notifications already tell whenever an object is allocated and of course you can get the stack (CLR Profiler does this today).&amp;nbsp; Using the notifications for allocations and for object motion you can easily track the object pointer over its lifetime and when it becomes finalized you can identify the stack that created it.&amp;nbsp; This one function was the missing piece.&amp;nbsp;&amp;nbsp; But even without it, the three techniques above often give good results.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=408650" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ricom/archive/tags/performance/default.aspx">performance</category><category domain="http://blogs.msdn.com/ricom/archive/tags/using+tools/default.aspx">using tools</category></item><item><title>Tracking down managed memory leaks (how to find a GC leak)</title><link>http://blogs.msdn.com/ricom/archive/2004/12/10/279612.aspx</link><pubDate>Fri, 10 Dec 2004 18:02:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:279612</guid><dc:creator>ricom</dc:creator><slash:comments>70</slash:comments><comments>http://blogs.msdn.com/ricom/comments/279612.aspx</comments><wfw:commentRss>http://blogs.msdn.com/ricom/commentrss.aspx?PostID=279612</wfw:commentRss><description>&lt;p&gt;If you think you've got memory leaks, or if you're just wondering what kind of stuff is on your heap you can follow the very same steps that I do and get fabulous results your friends will envy.&amp;nbsp; OK, well maybe not,&amp;nbsp; but they're handy anyway.&amp;nbsp; &lt;/p&gt; &lt;p&gt;These steps will help you to go from a suspected memory leak to the specific object references that are keeping your objects alive.&amp;nbsp; See the Resources at the end for the location of all these tools.&lt;/p&gt; &lt;p&gt;&lt;font size="4"&gt;Step 1:&amp;nbsp; Run your process and put it in the state you are curious about&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Be sure to choose a scenario you can reproduce if that's at all possible, otherwise you'll never know if you're making headway in clearing out the memory leaks.&lt;/p&gt; &lt;p&gt;&lt;font size="4"&gt;Step 2:&amp;nbsp; Use tasklist to find its process ID&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;C:\&amp;gt;tasklist&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;Image Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; PID Session Name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Session#&amp;nbsp;&amp;nbsp;&amp;nbsp; Mem Usage&lt;br /&gt;========================= ====== ================ ======== ============&lt;br /&gt;System Idle Process&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 RDP-Tcp#9&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 16 K&lt;br /&gt;System&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4 RDP-Tcp#9&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 112 K&lt;br /&gt;smss.exe&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 624 RDP-Tcp#9&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 252 K&lt;br /&gt;&amp;nbsp;...etc...&lt;br /&gt;ShowFormComplex.exe&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4496 RDP-Tcp#9&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 20,708 K&lt;br /&gt;tasklist.exe&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3636 RDP-Tcp#9&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4,180 K&lt;/font&gt;&lt;/p&gt; &lt;p&gt;From here we can see that my process is ID #4496&lt;/p&gt; &lt;p&gt;&lt;font size="4"&gt;Step 3:&amp;nbsp; Use VADump to get a summary of the process&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;C:\&amp;gt;vadump -sop 4496&lt;br /&gt;Category&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Total&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Private Shareable&amp;nbsp;&amp;nbsp;&amp;nbsp; Shared&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Pages&amp;nbsp;&amp;nbsp;&amp;nbsp; KBytes&amp;nbsp;&amp;nbsp;&amp;nbsp; KBytes&amp;nbsp;&amp;nbsp;&amp;nbsp; KBytes&amp;nbsp;&amp;nbsp;&amp;nbsp; KBytes&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Page Table Pages&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 35&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 140&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 140&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Other System&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 15&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Code/StaticData&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4596&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 18384&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4020&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3376&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10988&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Heap&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 215&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 860&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 860&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Stack&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 30&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 120&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 120&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Teb&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 16&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 16&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Mapped Data&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 129&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 516&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 24&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 492&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Other Data&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 157&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 628&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 624&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Total Modules&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4596&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 18384&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4020&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3376&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10988&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Total Dynamic Data&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 535&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2140&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1620&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 28&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 492&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Total System&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 50&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 200&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 200&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;br /&gt;Grand Total Working Set&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5181&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 20724&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 5840&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3404&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 11480&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Here we can see that the process is mostly code (18384k)&lt;/p&gt; &lt;p&gt;The vast majority of the resources that the CLR uses are under "Other Data" -- this is because the GC Heap is directly allocated with VirtualAlloc -- it doesn't go through a regular windows heap.&amp;nbsp; And same for the so-called "loader heaps" which hold type information and jitted code.&amp;nbsp; Most of the conventional "Heap" allocations are from whatever unmanaged is running.&amp;nbsp; In this case it's a winform application with piles of controls so there's storage associated with those things.&lt;/p&gt; &lt;p&gt;There isn't much "Other Data" here so the heap situation is probably pretty good but let's see where we stand on detailed CLR memory usage.&lt;/p&gt; &lt;p&gt;&lt;font size="4"&gt;Step 4:&amp;nbsp; Attach Windbg and load SOS&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;C:\&amp;gt; windbg -p 4496&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Once the debugger loads use this command to load our extension DLL&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;0:004&amp;gt; .loadby sos mscorwks&lt;/font&gt;&lt;/p&gt; &lt;p&gt;This tells the debugger to load the extension "sos.dll" from the same place that mscorwks.dll was loaded.&amp;nbsp; That ensures that you get the right version of SOS (it should be the one that matches the mscorwks you are using)&lt;/p&gt; &lt;p&gt;&lt;font size="4"&gt;Step 5:&amp;nbsp; Get the CLR memory summary&lt;/font&gt;&lt;/p&gt; &lt;p&gt;This command gives you a summary of what we've allocated.&amp;nbsp; The output will be a little different depending on the version of the runtime you are using.&amp;nbsp; But for a simple application you get the loader heaps for the two base domain structures (they just hold objects that can be shared in assorted ways) plus the storage for the first real appdomain (Domain 1).&amp;nbsp; And of course the jitted code.&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;0:004&amp;gt; !EEHeap&lt;br /&gt;Loader Heap:&lt;br /&gt;--------------------------------------&lt;br /&gt;System Domain: 5e093770&lt;br /&gt;...etc...&lt;br /&gt;Total size: 0x8000(32768)bytes&lt;br /&gt;--------------------------------------&lt;br /&gt;Shared Domain: 5e093fa8&lt;br /&gt;...etc...&lt;br /&gt;Total size: 0xa000(40960)bytes&lt;br /&gt;--------------------------------------&lt;br /&gt;Domain 1: 14f0d0&lt;br /&gt;...etc...&lt;br /&gt;Total size: 0x18000(98304)bytes&lt;br /&gt;--------------------------------------&lt;br /&gt;Jit code heap:&lt;br /&gt;LoaderCodeHeap: 02ef0000(10000:7000) Size: 0x7000(28672)bytes.&lt;br /&gt;Total size: 0x7000(28672)bytes&lt;br /&gt;--------------------------------------&lt;br /&gt;Module Thunk heaps:&lt;br /&gt;...etc...&lt;br /&gt;Total size: 0x0(0)bytes&lt;br /&gt;--------------------------------------&lt;br /&gt;Module Lookup Table heaps:&lt;br /&gt;...etc...&lt;br /&gt;Total size: 0x0(0)bytes&lt;br /&gt;--------------------------------------&lt;br /&gt;Total LoaderHeap size: 0x31000(200704)bytes&lt;/font&gt;&lt;/p&gt; &lt;p&gt;So here we've got 200k of stuff associated with what has been loaded, about 28k of which is jitted code.&amp;nbsp; &lt;/p&gt; &lt;p&gt;Next in the output (same command) is the summary of the GC Heap.&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;=======================================&lt;br /&gt;Number of GC Heaps: 1&lt;br /&gt;generation 0 starts at 0x00a61018&lt;br /&gt;generation 1 starts at 0x00a6100c&lt;br /&gt;generation 2 starts at 0x00a61000&lt;br /&gt;ephemeral segment allocation context: none&lt;br /&gt;&amp;nbsp;segment&amp;nbsp;&amp;nbsp;&amp;nbsp; begin allocated&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; size&lt;br /&gt;001b8630 7a8d0bbc&amp;nbsp; 7a8f08d8 0x0001fd1c(130332)&lt;br /&gt;001b4ac8 7b4f77e0&amp;nbsp; 7b50dcc8 0x000164e8(91368)&lt;br /&gt;00157690 02c10004&amp;nbsp; 02c10010 0x0000000c(12)&lt;br /&gt;00157610 5ba35728&amp;nbsp; 5ba7c4a0 0x00046d78(290168)&lt;br /&gt;00a60000 00a61000&amp;nbsp; 00aac000 0x0004b000(307200)&lt;br /&gt;Large object heap starts at 0x01a61000&lt;br /&gt;&amp;nbsp;segment&amp;nbsp;&amp;nbsp;&amp;nbsp; begin allocated&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; size&lt;br /&gt;01a60000 01a61000&amp;nbsp; 01a66d90 0x00005d90(23952)&lt;br /&gt;Total Size&amp;nbsp;&amp;nbsp; 0xcdd18(843032)&lt;br /&gt;------------------------------&lt;br /&gt;GC Heap Size&amp;nbsp;&amp;nbsp; 0xcdd18(843032)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;br /&gt;You will likely have many fewer small-heap segments than I did because I did this test on an internal debug build so there's funny things like a 12 byte segment in the dump.&amp;nbsp; But you'll see what segements there are and how big they are and you can see what the current boundaries are on the generations from which you can compute thier current exact size.&amp;nbsp; (Note that this is likely different than what the performance counters report as you can see in &lt;A href="http://blogs.msdn.com/maoni/archive/2004/06/03/148029.aspx"&gt;Maoni's blog &lt;/a&gt;-- those counters are budgeted from the last GC not the instanteous value -- it would be too expensive to keep updating the instantaneous value)&lt;/p&gt; &lt;p&gt;So in this case we can see that there is about 843k of GC heap.&amp;nbsp; Comparing that to the other data category there was about 2M total of other data.&amp;nbsp; The CLR accounts for about 1M of that.&amp;nbsp; The rest is likely bitmaps allocated from my winforms application's controls but whatever it is, it isn't CLR stuff...&lt;/p&gt; &lt;p&gt;&lt;font size="4"&gt;Step 5: Dump the GC Heap statistics&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Next we'll want to know, by type, what's on the heap at this exact instant&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;0:004&amp;gt; !DumpHeap -stat&lt;br /&gt;... sorted from smallest to biggest ... etc. etc...&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;7b586c7c&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 436&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10464 System.Internal.Gdi.WindowsGraphics&lt;br /&gt;5ba867ac&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 208&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 11648 System.Reflection.RuntimeMethodInfo&lt;br /&gt;7b586898&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 627&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 12540 System.Internal.Gdi.DeviceContext&lt;br /&gt;5baa4954&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 677&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 39992 System.Object[]&lt;br /&gt;5ba25c9c&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 8593&amp;nbsp;&amp;nbsp;&amp;nbsp; 561496 System.String&lt;br /&gt;Total 17427 objects&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Note that this dump includes both reachable and unreachable objects so unless you know that the GC just ran before you did this command you'll see some dead stuff in this report as well.&amp;nbsp; Sometimes its interesting and useful to force a GC to run before you do this so that you can get a summary of just the live stuff.&amp;nbsp; Sometimes it's useful to do dumps before and after forcing a GC so that you can see what sort of things are dying.&amp;nbsp; This may be a way to gather evidence that a forced GC is necessary.&amp;nbsp; See my blog on &lt;A href="http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx"&gt;When to Call GC.Collect()&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;So let's suppose that there weren't supposed to be 208 System.Reflection.RuntimeMethodInfo objects allocated here and that we thought that was a leak.&amp;nbsp; One of the things we'll want to do is to use CLR Profiler to see where those objects are being allocated -- that will give us half the picture.&amp;nbsp; But we can get the other half of the picture right here in the debugger.&lt;/p&gt; &lt;p&gt;&lt;font size="4"&gt;Step 6: Dump Type Specific Information&lt;/font&gt;&lt;/p&gt; &lt;p&gt;We can dump each object whose type name includes a given string with a simple command&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="1"&gt;&lt;font size="2"&gt;0:004&amp;gt; !DumpHeap -type System.Reflection.RuntimeMethodInfo&lt;br /&gt;&amp;nbsp;Address&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MT&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Size&lt;br /&gt;00a63da4 5baa62c0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 32&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;00a63e04 5baa6174&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 20&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;00a63e2c 5ba867ac&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 56&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;00a63e64 5baa5fa8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 16&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;00a63e88 5baa5fa8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 16&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;00a63f24 5baa6174&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 20&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;00a63f4c 5ba867ac&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 56&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;00a63f84 5baa5fa8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 16&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;etc. etc. etc.&lt;br /&gt;total 630 objects&lt;/font&gt;&lt;br /&gt;Statistics:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MT&amp;nbsp;&amp;nbsp;&amp;nbsp; Count TotalSize Class Name&lt;br /&gt;5baa62c0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 96 System.RuntimeType+RuntimeTypeCache+MemberInfoCache`1[[System.Reflection.RuntimeMethodInfo, mscorlib]]&lt;br /&gt;5baa5fa8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 211&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3376 System.Reflection.CerArrayList`1[[System.Reflection.RuntimeMethodInfo, mscorlib]]&lt;br /&gt;5baa6174&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 208&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4160 System.Collections.Generic.List`1[[System.Reflection.RuntimeMethodInfo, mscorlib]]&lt;br /&gt;5ba867ac&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 208&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 11648 System.Reflection.RuntimeMethodInfo&lt;br /&gt;Total 630 objects&lt;/font&gt;&lt;/p&gt; &lt;p&gt;Note that the type we wanted was System.Reflection.RuntimeMethodInfo and we can see that it has a method table 5ba867ac.&amp;nbsp; Those are the 56 byte objects. Now we can investigate some of these and see what is causing them to stay alive.&lt;/p&gt; &lt;p&gt;&lt;font size="4"&gt;Step 7: Identify the roots of suspected leaks&lt;/font&gt;&lt;/p&gt; &lt;p&gt;One of the lines in the dump was&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;00a63e2c 5ba867ac&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 56&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;/p&gt; &lt;p&gt;So that tells us there is an object of the type we want at address 00a63e2c.&amp;nbsp; Let's see what's keeping it alive&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;0:004&amp;gt; !gcroot 00a63e2c &lt;br /&gt;Scan Thread 0 OSTHread 1598&lt;br /&gt;Scan Thread 2 OSTHread 103c&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;DOMAIN(0014F0D0):&lt;br /&gt;HANDLE(WeakLn):3f10f0:&lt;br /&gt;Root:00a63d20(System.RuntimeType+RuntimeTypeCache)&lt;br /&gt;-&amp;gt;00a63da4(System.RuntimeType+RuntimeTypeCache+MemberInfoCache`1[[System.Reflection.RuntimeMethodInfo,mscorlib]])&lt;br /&gt;-&amp;gt;00a63e88(System.Reflection.CerArrayList`1[[System.Reflection.RuntimeMethodInfo, mscorlib]])&lt;br /&gt;-&amp;gt;00a63e98(System.Object[])&lt;br /&gt;-&amp;gt;00a63e2c(System.Reflection.RuntimeMethodInfo)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;&lt;font face="Courier New" size="2"&gt;DOMAIN(0014F0D0):&lt;br /&gt;HANDLE(Pinned):3f13ec:&lt;br /&gt;Root:01a64b50(System.Object[])&lt;br /&gt;-&amp;gt;00a62f20(System.ComponentModel.WeakEventHandlerList)&lt;br /&gt;-&amp;gt;00a63fb4(System.ComponentModel.WeakEventHandlerList+ListEntry)&lt;br /&gt;-&amp;gt;00a63ec4(System.ComponentModel.WeakEventHandlerList+ListEntry)&lt;br /&gt;-&amp;gt;00aa5f6c(System.ComponentModel.WeakDelegateHolder)&lt;br /&gt;-&amp;gt;00a63e2c(System.Reflection.RuntimeMethodInfo)&lt;/font&gt;&lt;/p&gt; &lt;p&gt;I've added some extra line breaks to the output above to make it easier to read but otherwise it's the raw output.&lt;/p&gt; &lt;p&gt;The gcroot command is trying to tell you if the object is reachable and if so how it is reached from each root.&amp;nbsp; The dump won't include all the ways the object is reachable but you do get at least one way to find the object -- usually that's enough.&amp;nbsp; If multiple paths are dumped they often have a common tail.&amp;nbsp; However the object is reachable (here it looks like maybe only weak references are left so this guy might go away on the next collect) that should give you a hint about (some of) the remaining references.&amp;nbsp; From there you can decide what pointers to null so that the object is properly released.&lt;/p&gt; &lt;p&gt;&lt;font size="4"&gt;Resources&lt;/font&gt;&lt;/p&gt; &lt;p&gt;You can get information on windbg at &lt;a href="http://www.microsoft.com/whdc/devtools/debugging/default.mspx"&gt;this location&lt;/a&gt;.&lt;br /&gt;Vadump has &lt;a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tools/tools/vadump.asp"&gt;usage information&lt;/a&gt;&amp;nbsp;and a &lt;a href="http://www.microsoft.com/windows2000/techinfo/reskit/tools/existing/vadump-o.asp"&gt;download&lt;/a&gt; from msdn and microsoft.com respectively.&lt;/p&gt; &lt;p&gt;If those links break, searching for windbg and vadump on the microsoft.com home page gave good results, that's how I got those links in the first place.&amp;nbsp; &lt;/p&gt; &lt;p&gt;CLR Profiler is available &lt;a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=86CE6052-D7F4-4AEB-9B7A-94635BEEBDDA&amp;amp;displaylang=en"&gt;here&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;It comes with documentation but there is additional material available in the &lt;a href="http://msdn.microsoft.com/perf"&gt;Performance PAG&lt;/a&gt;&amp;nbsp;in &lt;a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnpag/html/scalenethowto13.asp"&gt;Chapter 13&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=279612" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/ricom/archive/tags/performance/default.aspx">performance</category><category domain="http://blogs.msdn.com/ricom/archive/tags/using+tools/default.aspx">using tools</category></item></channel></rss>