<?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>Speaking of which... : GC</title><link>http://blogs.msdn.com/johan/archive/tags/GC/default.aspx</link><description>Tags: GC</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>Why doesn’t the GC kick in when the worker process is inactive?</title><link>http://blogs.msdn.com/johan/archive/2008/05/13/why-doesn-t-the-gc-kick-in-when-the-worker-process-is-inactive.aspx</link><pubDate>Tue, 13 May 2008 16:39:48 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8500269</guid><dc:creator>JohanS</dc:creator><slash:comments>1</slash:comments><comments>http://blogs.msdn.com/johan/comments/8500269.aspx</comments><wfw:commentRss>http://blogs.msdn.com/johan/commentrss.aspx?PostID=8500269</wfw:commentRss><description>&lt;p&gt; &lt;p&gt;I got the following question in my &lt;a href="http://blogs.msdn.com/johan/archive/2007/11/13/getting-started-with-windbg-part-i.aspx"&gt;Getting started with windbg&lt;/a&gt; – post and I thought it might be worth posting the replies in a separate article:&lt;/p&gt; &lt;p&gt;&lt;em&gt;Hi Johan,&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;about those threads with an ID of XXXX, should they go away after certain amount of idle time like 2 minutes? I am trouble shooting an application while the application should sit idle (because of no stimulation), however it is still using quite some CPU time and and threadpool threads, where it should not. So I created an adplus dump when it is sitting idle, and I found that there are many threads with ID XXXX, and I created a dump file again after 15 minutes, again, it still have the exact same XXXX threads. All those threads are completion port threads, and I wonder why they did not get recycled?&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;Also, when doing a '~*e !clrstack', most of the worker threads and completion port threads are showing "Failed to start stack walk: 80004005". Is there a way to show the stack for those threads, because those are the threads I am interested in.&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;Thank you!&lt;/em&gt;&lt;/p&gt; &lt;hr&gt;  &lt;p&gt;The GC will be triggered when you’re allocating memory. If your application is inactive, then so is the Garbage Collector. Even if you’re making a few, random allocations they still may not be enough to trigger a GC, so this is not at all unexpected.&lt;/p&gt; &lt;p&gt;The “Failed to start stack walk: 80004005”-error is displayed when the thread &lt;em&gt;did &lt;/em&gt;contain a managed stack, but no longer does.&lt;/p&gt; &lt;p&gt;/ Johan &lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8500269" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/johan/archive/tags/Performance/default.aspx">Performance</category><category domain="http://blogs.msdn.com/johan/archive/tags/WinDbg/default.aspx">WinDbg</category><category domain="http://blogs.msdn.com/johan/archive/tags/Managed+Heap/default.aspx">Managed Heap</category><category domain="http://blogs.msdn.com/johan/archive/tags/GC/default.aspx">GC</category><category domain="http://blogs.msdn.com/johan/archive/tags/Worker+Process/default.aspx">Worker Process</category><category domain="http://blogs.msdn.com/johan/archive/tags/ASP.NET/default.aspx">ASP.NET</category></item><item><title>The KB article every one should know about</title><link>http://blogs.msdn.com/johan/archive/2007/05/23/the-kb-article-every-one-should-know-about.aspx</link><pubDate>Wed, 23 May 2007 18:23:46 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2817754</guid><dc:creator>JohanS</dc:creator><slash:comments>9</slash:comments><comments>http://blogs.msdn.com/johan/comments/2817754.aspx</comments><wfw:commentRss>http://blogs.msdn.com/johan/commentrss.aspx?PostID=2817754</wfw:commentRss><description>&lt;p&gt;Have you read &lt;a href="http://support.microsoft.com/?kbid=307340"&gt;http://support.microsoft.com/?kbid=307340&lt;/a&gt;? If not, I suggest you do so. If you need convincing or simply want to know why this problem occurs I suggest you keep reading.&lt;/p&gt; &lt;p&gt;The problem described in the article above can cause your application to spike in CPU time, Memory usage or both. This will lead to your application hanging or possibly even crashing. The cause of the problem is&amp;nbsp;one of the most basic operations, and is something that we do very frequently.&lt;/p&gt; &lt;p&gt; &lt;hr&gt;  &lt;p&gt;&lt;/p&gt; &lt;h1&gt;The problem&lt;/h1&gt; &lt;p&gt;The problem in a nutshell is that string concatenation is bad. I don't mean bad as in "You shouldn't eat that extra piece of chocolate". I mean it as in "You really shouldn't put your head in wet concrete". If you've been reading my previous post on &lt;a href="http://blogs.msdn.com/johan/archive/2007/04/20/memory-management-in-the-net-framework.aspx"&gt;memory management&lt;/a&gt;&amp;nbsp;you might be able to figure out why string concatenation could be such a potential hazard. Consider the following scenario:&lt;/p&gt; &lt;p&gt;You're getting ready to go grocery shopping. You take a sheet of paper and a pen and begin writing down what you need to buy. The first item that crosses your mind is milk, so you write it down. Now you remember that you're out of bread as well, so you throw away the first sheet of paper, pick out a new one and write down milk and bread. You then throw away this paper too and write down milk, bread and&amp;nbsp;apples on a third piece of paper. You throw away the third piece of paper and repeat until finally the list is done. If you have 20 items on your list you now have 19 discarded papers lying on the floor waiting for the "garbage collector".&lt;/p&gt; &lt;p&gt;This is exactly how regular string concatenation works in .NET. Every time you concatenate a string the framework needs to allocate a new memory segment, large enough to hold the result of the concatenation and store the string in this new segment. The old segment is flagged as ready for garbage collection and as we all know garbage collection costs CPU and pending garbage collection costs memory.&lt;/p&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;A real&amp;nbsp;example&lt;/h1&gt; &lt;p&gt;So if you thought that 19 sheets of paper were a waste consider the following piece of code:&lt;/p&gt; &lt;div class="SampleCode"&gt;private String buildTable()&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;String sReturn;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;int i, j;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;sReturn = "&amp;lt;table&amp;gt;"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (i = 0; i &amp;lt; 35; i++) // Rows&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sReturn += "&amp;lt;tr&amp;gt;"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (j = 0; j &amp;lt; 15; j++) // Columns&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sReturn += "&amp;lt;td&amp;gt;"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sReturn += i.ToString() + " : " + j.ToString();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sReturn += "&amp;lt;/td&amp;gt;"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sReturn += "&amp;lt;/tr&amp;gt;"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;sReturn += "&amp;lt;/table&amp;gt;"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;return sReturn;&lt;br&gt;} &lt;/div&gt; &lt;p&gt;All in all we have 2697 concatenations which means we have 2696 unnecessary allocations. The allocations will be gradually bigger and bigger, so the last ones will be almost as big as the final table. Needless to say this will consume a lot of unnecessary memory.&amp;nbsp;Also, if this table was built for a "real" application it would probably contain a lot more data for each cell, not to mention custom styles and classes. Imagine what would happen when the string goes past the magic &lt;a href="http://blogs.msdn.com/johan/archive/2007/04/20/memory-management-in-the-net-framework.aspx"&gt;85000 bytes barrier&lt;/a&gt; that defines what goes on the large object heap and what doesn't. In a larger scale this would have a &lt;em&gt;severe&lt;/em&gt; impact on performance.&lt;/p&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;This is a really weird behavior, how can it be?!&lt;/h1&gt; &lt;p&gt;It's not that odd really. If we extend the grocery shopping analogy a little we can see why there's a logical reason for this: You know that you want to write something down. You have a limited amount of paper, so what do you do? You cut out a piece of paper just large enough for you to write what you need. When you find that you need to add another item to the list you'll have to discard the old piece and cut out a new one. Not very practical, agreed, but you're trying to minimize your paper usage.&lt;/p&gt; &lt;p&gt;As the clever beings that we are we normally make a rough estimate of how big a piece of paper we need and then start writing. The framework, however, has no way of estimating how big a string is going to be. It doesn't know if you're going to concatenate it zero or ten thousand time, so that's something you'll have to estimate yourself.&lt;/p&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;So what should I do instead?&lt;/h1&gt; &lt;p&gt;One word: StringBuilder&lt;/p&gt; &lt;p&gt;The StringBuilder class is made especially for this type of operations. When using the StringBuilder class our sample above would look something like this&lt;/p&gt; &lt;div class="SampleCode"&gt;private String buildTableSB()&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp; StringBuilder sReturn = new StringBuilder();&lt;br&gt;&amp;nbsp;&amp;nbsp; sReturn.Capacity = 8000; // This is optional, but may be very efficient&lt;br&gt;&amp;nbsp;&amp;nbsp; int i, j;&lt;br&gt;&amp;nbsp;&amp;nbsp; sReturn.Append("&amp;lt;table&amp;gt;");&lt;br&gt;&amp;nbsp;&amp;nbsp; for (i = 0; i &amp;lt; 35; i++) // Rows&lt;br&gt;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; sReturn.Append("&amp;lt;tr&amp;gt;");&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; for (j = 0; j &amp;lt; 15; j++) // Columns&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; sReturn.Append("&amp;lt;td&amp;gt;");&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; sReturn.Append(i.ToString());&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; sReturn.Append(" : ");&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; sReturn.Append(j.ToString());&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; sReturn.Append("&amp;lt;/td&amp;gt;");&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; sReturn.Append("&amp;lt;/tr&amp;gt;");&lt;br&gt;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp; sReturn.Append("&amp;lt;/table&amp;gt;");&lt;br&gt;&amp;nbsp;&amp;nbsp; return sReturn.ToString;&lt;br&gt;}&lt;/div&gt; &lt;p&gt;The difference in performance for a larger operation is enormous. I highly recommend trying out these samples. Copy the functions to a winforms/webforms application, increase the number of rows to 500 and run both versions. The&amp;nbsp;StringBuilder version will finish&amp;nbsp;within milliseconds, and the "classic" version will not...&amp;nbsp;For additional tidbits, take a look at the GC counter in performance monitor and look at how many GCs you have to make to complete the operation.&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Bye for now / Johan&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2817754" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/johan/archive/tags/OutOfMemory/default.aspx">OutOfMemory</category><category domain="http://blogs.msdn.com/johan/archive/tags/Performance/default.aspx">Performance</category><category domain="http://blogs.msdn.com/johan/archive/tags/Managed+Heap/default.aspx">Managed Heap</category><category domain="http://blogs.msdn.com/johan/archive/tags/GC/default.aspx">GC</category><category domain="http://blogs.msdn.com/johan/archive/tags/ASP.NET/default.aspx">ASP.NET</category></item><item><title>Finalizers and weak references</title><link>http://blogs.msdn.com/johan/archive/2007/04/26/finalizers-and-weak-references.aspx</link><pubDate>Thu, 26 Apr 2007 17:25:57 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2286964</guid><dc:creator>JohanS</dc:creator><slash:comments>2</slash:comments><comments>http://blogs.msdn.com/johan/comments/2286964.aspx</comments><wfw:commentRss>http://blogs.msdn.com/johan/commentrss.aspx?PostID=2286964</wfw:commentRss><description>&lt;p&gt;What do finalizers and weak references have in common? Well more than you might think&amp;nbsp;actually.&lt;/p&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;Finalizers&lt;/h1&gt; &lt;p&gt;Finalizers are clean-up code that will be run at the end of an objects life-cycle. You should only release native resources in the finalizer. When you use your object you should not the finalizer cleaning up after you. You're much better off performance-wise by making your object Disposable and making sure that you're actually calling the Dispose method. The finalizers should only be the final fail-safe in case the Dispose call is forgotten.&lt;/p&gt; &lt;p&gt;Having a finalizer means your object is &lt;em&gt;always&lt;/em&gt; promoted at least one generation by the Garbage Collector (GC). Here's why:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;You have an object with a finalizer and the object is no longer referenced  &lt;li&gt;The GC comes to collect the object. It sees that the object has a finalizer. The GC therefore adds the object to the finalizer queue and promotes it one generation on the heap  &lt;li&gt;The finalizer is run, once the finalizer thread is available. (If this takes too long the object might actually be promoted a second time.)  &lt;li&gt;The object is finally garbage collected&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Since you only have one finalizer thread and rely heavily on finalizers you may be creating an unintentional bottleneck.&lt;/p&gt; &lt;h2&gt;So shouldn't I use finalizers?&lt;/h2&gt; &lt;p&gt;Well sure you should. But as I've said before, only regard them as a fail-safe. Make your objects disposable and add a finalizer just in case somebody less skilled than yourself forgets to call the Dispose method. :-)&lt;/p&gt; &lt;hr&gt; &lt;/hr&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;Weak References&lt;/h1&gt; &lt;p&gt;A weak reference is different from a strong reference since this reference is disregarded when the GC comes to collect the object, so if there are nothing but weak references remaining for an object when the heap is GC' d the object will be collected. Weak references are generally useful in two ways:&lt;/p&gt; &lt;h2&gt;1. Poor mans cache&lt;/h2&gt; &lt;p&gt;If you have a weak reference to an object, say a dataset that takes some time putting together, then you can still work with that object as long as it hasn't been garbage collected. You would work with the reference as you would with any cached item. First you check if the object still exists, if it does, then you get a strong reference to the object&amp;nbsp;and work with it. If not, then you create a new dataset and and work with that one.&lt;/p&gt; &lt;h2&gt;2. Custom finalizer&lt;/h2&gt; &lt;p&gt;Consider the following scenario. (No, you're not having Déjà Vu. This is the same scenario I described in the finalizers section above.):&lt;/p&gt; &lt;ul&gt; &lt;li&gt;You have an object with a finalizer and the object is no longer referenced  &lt;li&gt;The GC comes to collect the object. It sees that the object has a finalizer. The GC therefore adds the object to the finalizer queue and promotes it one generation on the heap  &lt;li&gt;The finalizer is run, once the finalizer thread is available. (If this takes too long the object might actually be promoted a second time.)  &lt;li&gt;The object is finally garbage collected&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Here's an alternative way of doing the same thing:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;You don't use a finalizer. Instead you have an array of weak references to objects that you occasionally check  &lt;li&gt;When you find that one of the weak references' target&amp;nbsp;is null that means the GC has collected that object  &lt;li&gt;The object has been garbage collected, so now you run your own clean-up code.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Okay, so the second alternative has two obvious benefits compared to the first:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Your object won't be automatically promoted to generation 1 or generation 2.  &lt;li&gt;You won't be depending on the finalizer thread being available to run your clean-up code.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;The ultimate option would off course be to have your object being Disposable, make sure you call the Dispose method and use alternatives one or two as back-ups.&lt;/p&gt; &lt;hr&gt; &lt;/hr&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;Long Weak References&lt;/h1&gt; &lt;p&gt;Long weak references will survive the finalizer, so even if your object has been finalized you can still reference it. This may be useful in certain situations, but it can be risky if you're not 100% sure what the finalizer does. Let's say your object performs some kind of file I/O and all file references are closed by the finalizer. If you reference this object using a Long Weak Reference you will have an instance of the object with all I/O functionality disabled. Obviously this could cause all sorts of exceptions if you don't know what you're doing.&lt;/p&gt; &lt;hr&gt; &lt;/hr&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;Myths and Facts&lt;/h1&gt; &lt;p&gt;I've previously gotten some positive feedback on the Myth / Fact format, so I thought I'd round this topic off by adding a summary using that format. Here goes:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Myth:&lt;br&gt;&lt;/strong&gt;You can pretty much do anything in the finalizer.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Fact:&lt;br&gt;&lt;/strong&gt;The only thing you &lt;em&gt;should&lt;/em&gt; do in the finalizer is to release your native resources. The reason why your doing this is because you can never be sure that the person using your object is smart enough to call the Dispose method. If everybody did do that, then we wouldn't need the finalizer at all&lt;/p&gt; &lt;p&gt;&lt;br&gt;&lt;strong&gt;Myth:&lt;/strong&gt;&lt;br&gt;If you have a finalizer you don't need to make your component disposable&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Fact:&lt;/strong&gt;&lt;br&gt;You should &lt;em&gt;always&lt;/em&gt; call the Dispose method of any disposable object. It doesn't matter if all it seems to do is set GC.SuppressFinalize(Me). That is no guarantee that the next version of the object won't do all kinds of stuff in the Dispose method. The rule of thumb is: If it is disposable, dispose it.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;br&gt;Myth:&lt;/strong&gt;&lt;br&gt;Having a finalizer has no impact on performance. When the Garbage Collector (GC) comes to collect it the GC will simply call the Finalize method first and then collect it.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Fact:&lt;br&gt;&lt;/strong&gt;Having a finalizer is a &lt;em&gt;guarantee&lt;/em&gt; that your object will be promoted one generation before it is collected by the GC. When the GC comes across an object with a finalizer that is ready to be collected it puts the object in the finalizer queue, but the object is then moved to the next generation. For more information on this, please see my &lt;a href="http://blogs.msdn.com/johan/archive/2007/04/20/memory-management-in-the-net-framework.aspx"&gt;previous post&lt;/a&gt; on the managed heap, the GC and its generations. You also have only one finalizer thread, so this&amp;nbsp;has&amp;nbsp;bottleneck warning written all over it.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&lt;br&gt;Myth:&lt;br&gt;&lt;/strong&gt;Long Weak References are available as long as the object is in the finalizer queue, but they're released once the&amp;nbsp;Finalize method is called.&lt;/p&gt; &lt;p&gt;&lt;strong&gt;Fact:&lt;br&gt;&lt;/strong&gt;Nope. They &lt;em&gt;are&lt;/em&gt; available when they're in the finalizer queue, but they're &lt;em&gt;also&lt;/em&gt; available &lt;em&gt;after&lt;/em&gt; the finalizer has been called. You really should be aware of this or you might get the strangest results.&lt;/p&gt; &lt;hr&gt; &lt;/hr&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;Jedi skills&lt;/h1&gt; &lt;p&gt;If you want to know more about finalizers, weak references, the GC and other goodies I highly recommend checking out &lt;a href="http://blogs.msdn.com/maoni/default.aspx"&gt;Maoni's WebLog&lt;/a&gt;. What better place to learn about this than from one of the developers?&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Over and out&lt;/p&gt; &lt;p&gt;/ Johan&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2286964" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/johan/archive/tags/Performance/default.aspx">Performance</category><category domain="http://blogs.msdn.com/johan/archive/tags/Managed+Heap/default.aspx">Managed Heap</category><category domain="http://blogs.msdn.com/johan/archive/tags/GC/default.aspx">GC</category><category domain="http://blogs.msdn.com/johan/archive/tags/Finalizers/default.aspx">Finalizers</category><category domain="http://blogs.msdn.com/johan/archive/tags/Weak+References/default.aspx">Weak References</category><category domain="http://blogs.msdn.com/johan/archive/tags/ASP.NET/default.aspx">ASP.NET</category></item><item><title>Memory management in the .NET Framework</title><link>http://blogs.msdn.com/johan/archive/2007/04/20/memory-management-in-the-net-framework.aspx</link><pubDate>Fri, 20 Apr 2007 16:07:29 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:2206941</guid><dc:creator>JohanS</dc:creator><slash:comments>12</slash:comments><comments>http://blogs.msdn.com/johan/comments/2206941.aspx</comments><wfw:commentRss>http://blogs.msdn.com/johan/commentrss.aspx?PostID=2206941</wfw:commentRss><description>&lt;p&gt;This is a subject that has been covered before and I have no intention of writing the ultimate post on the subject. Still I think this is something that every good developer should know.&lt;/p&gt; &lt;h1&gt;Why do I need to know this?&lt;/h1&gt; &lt;p&gt;My colleagues and I are quite often asked about the necessity of knowing how the Garbage Collector (GC) works. After all, it isn't really necessary to know what's going on behind the scenes, right..? A developer shouldn't have to bother about how the framework works..? Or should he..?&lt;/p&gt; &lt;p&gt;Well you may be a pretty good bus driver even if you know nothing about engines. Basic stuff like not stepping on the break and the gas at the same time is probably enough. If you want to do F1 racing, on the other hand, you will want to know as much as you can about your car. So, if you're content doing small scale web applications or not so efficient winforms applications, then you probably don't have to bother. If you want to do front-line work, then you've got to get your hands dirty.&lt;/p&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;The different generations&lt;/h1&gt; &lt;p&gt;In order to be effective the managed heap is trying to figure out which objects it can disregard and only check up on occasionally. Some objects may span the entire lifecycle of the application and others may be very short lived, so if an object stays alive for&amp;nbsp;a longer period of time the GC will check up on it less frequently. This is accomplished by dividing the&amp;nbsp;managed heap into three generations. Generations 0, 1 and 2.&amp;nbsp;An object will begin in generation 0 and if it is&amp;nbsp;in use for a long period of time it will travel through generations&amp;nbsp;0 to 2. A possible metaphor for this scenario would the following:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;You're asked to call a customer. You're pretty good with numbers&amp;nbsp;so you keep the phone number in your head.&amp;nbsp;(Generation 0)  &lt;li&gt;There is no reply and so you need to remember the phone number a little longer. Meanwhile you're given additional numbers to call.  &lt;li&gt;You know from past experience that your limit for keeping numbers in your head is 10, so once you reach that critical point you decide to write down the numbers you still&amp;nbsp;need on post-it notes (Generation 1)  &lt;li&gt;As the day goes on you keep transferring numbers from memory to post-its.  &lt;li&gt;Your desktop has now reached maximum capacity. It is cluttered with post-it notes, so you sort through them, determining which notes you still need. You throw away the ones&amp;nbsp;that are obsolete&amp;nbsp;and write down the remaining to your address book. (Generation 2)&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Like I stated above; when an object is created it is put in Generation 0. The initial size limit of Generation 0 is determined by the size of the processor cache. This is dynamically changed depending on the allocation rate of the application. Once Generation 0 reaches its limit it will go through all the items in Generation 0, tag the obsolete objects for collection and remove them. This is called the Mark and Sweep phase. Everything that survives this sweep will be compacted and moved to Generation 1. The size limit of Generation 1 is also determined by the allocation rate of the application, and once it is reached a Generation 1 collection will occur. This will first mark and sweep all items in Generation 1, moving the surviving items to Generation 2, and then mark and sweep the items in Generation 0.&lt;/p&gt; &lt;p&gt;The healthy ratio between GC's in the different generations is approximately 100 - 10 - 1, so for 100 Generation 0 GC's you normally have 10 GC's of Generation 1 and 1 of Generation 2. A normal Generation 0 GC usually takes a few milliseconds. Performing a GC of Generation 1 rarely takes more than 30 milliseconds,&amp;nbsp;but a Generation 2 collection can take&amp;nbsp;quite some time depending on the application.&lt;/p&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;The Large Object Heap&lt;/h1&gt; &lt;p&gt;To top it off we also have what is called "The Large Object Heap" (LOH). This is where anything larger than 85.000 bytes will be stored. The&amp;nbsp;LOH is collected each time a new segment needs to be reserved (see below), but it is &lt;em&gt;not&lt;/em&gt; compacted like generations 0, 1 and 2. When you perform a collection on the LOH you will also perform a GC on the other generations.&lt;/p&gt; &lt;p&gt;Now, if the limit is at 85.000 bytes, won't most of my objects end up on the LOH? - Well not necessarily. For example: A dataset may contain lots and lots of data, but the dataset object only contains references to other objects. The data in the columns are each stored in individual strings and as long as they're not larger than 85 KB you're clear.&lt;/p&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;Memory segments&lt;/h1&gt; &lt;p&gt;The managed heap will reserve memory in segments. The size of the segments depend on the configuration. If &lt;span class="InlineCode"&gt;&amp;lt;gcServer enabled="true"/&amp;gt;&lt;/span&gt; you will reserve memory in 64 MB segments, otherwise you'll be doing it in 32 MB segments. LOH are reserved in 16 MB segments. Only Generation 2 and the LOH will span several segments.&lt;/p&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;What happens during GC?&lt;/h1&gt; &lt;p&gt;Let's say we're performing a full GC, including the LOH. This is what happens:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;The objects on the LOH&amp;nbsp;are &lt;em&gt;Marked&lt;/em&gt;. Each item in the LOH is checked for references. If none are found it's ready to be collected.  &lt;li&gt;The LOH is &lt;em&gt;Swept&lt;/em&gt;. All marked objects are released from memory.  &lt;li&gt;The LOH is &lt;strong&gt;not&lt;/strong&gt; compacted.  &lt;li&gt;Generation 2 is &lt;em&gt;Marked.&lt;/em&gt;  &lt;li&gt;Generation 2 is &lt;em&gt;Swept.&lt;/em&gt;  &lt;li&gt;Generation 2 is compacted. (Imagine removing a few books from your bookcase, and then pushing the remaining books together freeing up continuous space at the end.)  &lt;li&gt;Generation 1 is &lt;em&gt;Marked.&lt;/em&gt;  &lt;li&gt;Generation 1 is &lt;em&gt;Swept.&lt;/em&gt; &lt;li&gt;Everything &amp;nbsp;that survived the sweep is compacted. &lt;li&gt;The pointer for where Generation 2 ends is updated. Everything that survived the sweep is now Generation 2. &lt;li&gt;Generation 0 is &lt;em&gt;Marked.&lt;/em&gt;  &lt;li&gt;Generation 0 is &lt;em&gt;Swept.&lt;/em&gt; &lt;li&gt;Everything that survived the sweep is compacted. &lt;li&gt;The pointer for where Generation 1 ends is updated. Everything that survived the sweep is now Generation 1.&lt;/li&gt;&lt;/ul&gt; &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;A few quick tips&lt;/h1&gt; &lt;p&gt;This topic could off course be a &lt;em&gt;lot&lt;/em&gt; bigger, but here are some quick suggestions.&lt;/p&gt; &lt;h2&gt;Try to stay out of Generation 1&lt;/h2&gt; &lt;p&gt;Well off course Generation 1 is better than Generation 2, but you should aim for keeping only a select few objects in Generation 2. Those should be variables that are defined at the beginning of the application lifecycle and released at the end. In an ideal world all other variables&amp;nbsp;should be of the hit-and-run variety and never leave Generation 0.&lt;/p&gt; &lt;h2&gt;Don't call GC.Collect()&lt;/h2&gt; &lt;p&gt;This has been said before. It will be said again, and again, and again. You should &lt;em&gt;almost&lt;/em&gt; never call GC.Collect() manually. And by &lt;em&gt;almost&lt;/em&gt; I mean once in a lifetime, not once per application, and certainly not once per function call. I occasionally call GC.Collect() for testing purposes just to see if memory has been released. Normally you would never call it. The GC is self-balancing and by calling GC.Collect() you are disrupting that balance. Think of it as tampering with the eco-system, pouring sugar in the gasoline or whatever metaphor you prefer. :-)&lt;/p&gt; &lt;h2&gt;Avoid large objects&lt;/h2&gt; &lt;p&gt;If you can stay below the 85.000 byte limit, then do so. If not, consider reusing the&amp;nbsp;object.&amp;nbsp;When it comes to large objects it's better&amp;nbsp;to use one for a long time than to use many for short periods of time.&lt;/p&gt; &lt;h2&gt;Don't use finalizers&lt;/h2&gt; &lt;p&gt;When your object has a finalizer the finalize method will be called when the object is no longer alive. So far so good. Unfortunately&amp;nbsp;your object will be passed into the next generation,&amp;nbsp;since it's not yet ready to be collected. This means that all objects with a finalizer will &lt;em&gt;at least&lt;/em&gt; end up in Generation 1. Most likely in Generation 2.&lt;/p&gt; &lt;p&gt;Well, as I said: There is a lot more to cover on this. There are books to be written and songs to be sung, but you've got to draw the line somewhere. I will most certainly cover more of this in future posts.&lt;/p&gt; &lt;p&gt;/ Johan&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=2206941" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/johan/archive/tags/OutOfMemory/default.aspx">OutOfMemory</category><category domain="http://blogs.msdn.com/johan/archive/tags/Performance/default.aspx">Performance</category><category domain="http://blogs.msdn.com/johan/archive/tags/Managed+Heap/default.aspx">Managed Heap</category><category domain="http://blogs.msdn.com/johan/archive/tags/GC/default.aspx">GC</category><category domain="http://blogs.msdn.com/johan/archive/tags/ASP.NET/default.aspx">ASP.NET</category></item><item><title>I am getting OutOfMemoryExceptions. How can I troubleshoot this?</title><link>http://blogs.msdn.com/johan/archive/2007/01/11/i-am-getting-outofmemoryexceptions-how-can-i-troubleshoot-this.aspx</link><pubDate>Thu, 11 Jan 2007 19:46:47 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:1451030</guid><dc:creator>JohanS</dc:creator><slash:comments>12</slash:comments><comments>http://blogs.msdn.com/johan/comments/1451030.aspx</comments><wfw:commentRss>http://blogs.msdn.com/johan/commentrss.aspx?PostID=1451030</wfw:commentRss><description>&lt;h1&gt;Problem:&lt;/h1&gt; &lt;p&gt;You've written an ASP.NET application that is getting OutOfMemoryExceptions.&lt;/p&gt; &lt;h1&gt;Cause:&lt;/h1&gt; &lt;p&gt;Let's find out...&lt;/p&gt; &lt;h1&gt;Resolution:&lt;/h1&gt; &lt;p&gt;Use Windbg to take a look at the heap.&lt;/p&gt; &lt;hr&gt;  &lt;h1&gt;&amp;nbsp;&lt;/h1&gt; &lt;h1&gt;Is it a leak?&lt;/h1&gt; &lt;p&gt;Take a look at the memory usage of you application using perfmon. If memory is slowly increasing and never released, then you have a leak. If it is going up and down like a rollercoaster, then you are most likely using huge amounts of memory for certain operations which is later garbage collected.&lt;/p&gt; &lt;h1&gt;How do I troubleshoot this?&lt;/h1&gt; &lt;p&gt;Below I'll try to give you a step by step description of how to troubleshoot this scenario&lt;/p&gt; &lt;h2&gt;1. Get a memory dump&lt;/h2&gt; &lt;p&gt;This is done using Windbg and Adplus. If you don't have Windbg installed, check out my &lt;a href="http://blogs.msdn.com/johan/archive/2007/01/11/how-to-install-windbg-and-get-your-first-memory-dump.aspx"&gt;previous post&lt;/a&gt;.&lt;/p&gt; &lt;h2&gt;&amp;nbsp;&lt;/h2&gt; &lt;h2&gt;2. Open up the dump and load SOS&lt;/h2&gt; &lt;p&gt;Open the dump in Windbg and load the SOS extension. You'll find it in the framework directory so if you're debugging an application for Framework 2.0 look in "C:\Windows\Microsoft.NET\Framework\v2.0.50727". Anyway, load SOS by typing:&lt;/p&gt; &lt;div class="SampleCode"&gt;.load [path]\sos&lt;/div&gt; &lt;h2&gt;&amp;nbsp;&lt;/h2&gt; &lt;h2&gt;&amp;nbsp;&lt;/h2&gt; &lt;h2&gt;3. Run dumpheap&lt;/h2&gt; &lt;p&gt;Execute the following command:&lt;/p&gt; &lt;div class="SampleCode"&gt;!dumpheap -stat&lt;/div&gt; &lt;p&gt;It will show statistics for the objects on the heap in a nice little summary divided in tho four columns.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;The method table of the object  &lt;li&gt;The number of objects of this type on the heap  &lt;li&gt;The total size of these objects in bytes  &lt;li&gt;The name of the object type&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Be careful not to omit the -stat parameter. If you do then Windbg will dump the address of each object in the entire heap to your screen, which will be a lot of information to say the least.&lt;/p&gt; &lt;h1&gt;Analyzing the information provided by !dumpheap&lt;/h1&gt; &lt;p&gt;Here's a sample heap from one of my cases... &lt;/p&gt; &lt;div class="SampleCode"&gt;0:000&amp;gt; !dumpheap -stat&lt;br&gt;Statistics:&lt;br&gt;MT&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Count &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;TotalSize Class Name&lt;br&gt;7a787cc4&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;1 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 12 System.IO.FileSystemWatcher+FSWAsyncResult&lt;br&gt;7a75904c &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;1 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 12 System.Diagnostics.OrdinalCaseInsensitiveComparer&lt;br&gt;7a7575cc &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 12 System.CodeDom.Compiler.CodeDomConfigurationHandler&lt;br&gt;7a7571a8 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 12 System.Net.DefaultCertPolicy&lt;br&gt;7a75661c &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp; 12 System.Diagnostics.TraceListenerCollection&lt;br&gt;7a755834 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; 12 System.Diagnostics.PerformanceCounterCategoryType&lt;br&gt;........CONTINUED.........&lt;br&gt;68a66a88&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 227,559&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;12,743,304 System.Web.UI.WebControls.Literal&lt;br&gt;68a2f7fc &amp;nbsp;&amp;nbsp;&amp;nbsp; 399,272 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;14,373,792 System.Web.UI.ControlCollection&lt;br&gt;68a92e2c &amp;nbsp;&amp;nbsp;&amp;nbsp; 768,731 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;33,824,164 System.Web.UI.Control+OccasionalFields&lt;br&gt;68a884a0 &amp;nbsp;&amp;nbsp;&amp;nbsp; 641,952 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;38,517,120 System.Web.UI.LiteralControl&lt;br&gt;79124228 &amp;nbsp;&amp;nbsp;&amp;nbsp; 879,515 &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;43,394,976 System.Object[]&lt;br&gt;790fa3e0&amp;nbsp;&amp;nbsp;&amp;nbsp;1,431,594&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 122,806,484 System.String&lt;br&gt;&lt;br&gt;Total 10,389,625 objects, Total size: 463,313,540 &lt;/div&gt; &lt;p&gt;So in this particular dump I have 1,431,594 strings whose total size is 122 MBytes, 879,515 Objects with a total size of&amp;nbsp;43 MBytes, etc.&lt;/p&gt; &lt;h2&gt;Objects in heap may be larger than they appear&lt;/h2&gt; &lt;p&gt;The TotalSize column isn't 100% true. Look at the LiteralControls that are on third place. They only use a total of 38 MBytes, or do they? The TotalSize refers to the object structure, but the member variables like strings, integers and other&amp;nbsp;child objects are not included. It kind of makes sense, since otherwise the Total size would be completely off the scale. The LiteralControl objects contain a number of child objects and three of those are strings. Their respective size is listed with the System.String object.&lt;/p&gt; &lt;h2&gt;Using !dumpobj&lt;/h2&gt; &lt;p&gt;Let's take a closer look at one of the System.Web.UI.LiteralControls. We list all of the controls with the following command &lt;span class="InlineCode"&gt;!dumpheap -type System.Web.UI.LiteralControl&lt;/span&gt; and quickly press Ctrl+Break before the screen fills with too many lines:&lt;/p&gt; &lt;div class="SampleCode"&gt;0:000&amp;gt; !dumpheap -type System.Web.UI.LiteralControl&lt;br&gt;&amp;nbsp;Address&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MT Size Gen&lt;br&gt;023ea0a8 68a884a0&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp; 2 System.Web.UI.LiteralControl &lt;br&gt;023ea0e4 68a884a0&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp; 2 System.Web.UI.LiteralControl &lt;br&gt;023ea374 68a884a0&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp; 2 System.Web.UI.LiteralControl &lt;br&gt;023ea460 68a884a0&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp; 2 System.Web.UI.LiteralControl &lt;br&gt;023ea510 68a884a0&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp; 2 System.Web.UI.LiteralControl &lt;br&gt;023eab3c 68a884a0&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp; 2 System.Web.UI.LiteralControl &lt;br&gt;........CONTINUED........&lt;br&gt;023fe31c 68a884a0&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp; 2 System.Web.UI.LiteralControl &lt;br&gt;023fe414 68a884a0&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp; 2 System.Web.UI.LiteralControl &lt;br&gt;023fe4c4 68a884a0&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp; 2 System.Web.UI.LiteralControl &lt;br&gt;023fe500 68a884a0&amp;nbsp;&amp;nbsp; 60&amp;nbsp;&amp;nbsp; 2 System.Web.UI.LiteralControl &lt;/div&gt; &lt;p&gt;As you can see each LiteralControl is 60 bytes in size. Like I said before that's the size of the object structure alone, not it's referenced objects and properties. We now pick the address of one of the LiteralControls and execute the &lt;span class="InlineCode"&gt;!dumpobj&lt;/span&gt; command (&lt;span class="InlineCode"&gt;!do&lt;/span&gt; for short). This gives us the following result:  &lt;div class="SampleCode"&gt;0:000&amp;gt; !do 023ea0a8 &lt;br&gt;Name: System.Web.UI.LiteralControl&lt;br&gt;MethodTable: 68a884a0&lt;br&gt;EEClass: 68a88428&lt;br&gt;Size: 60(0x3c) bytes&lt;br&gt;GC Generation: 2&lt;br&gt;(C:\WINDOWS\assembly\GAC_32\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)&lt;br&gt;Fields:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MT&amp;nbsp;&amp;nbsp;&amp;nbsp; Field Offset&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; Type&amp;nbsp;&amp;nbsp; VT&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Attr&amp;nbsp;&amp;nbsp;&amp;nbsp; Value Name&lt;br&gt;790fa3e0&amp;nbsp; 4001fe0&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;&amp;nbsp;&amp;nbsp;System.String&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0 instance 00000000 _id&lt;br&gt;790fa3e0&amp;nbsp; 4001fe1&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; 8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.String&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 instance 00000000 _cachedUniqueID&lt;br&gt;68a2af44&amp;nbsp; 4001fe2&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; c&amp;nbsp;&amp;nbsp; ...em.Web.UI.Control&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0 instance 023e8864 _parent&lt;br&gt;68a91070&amp;nbsp; 4001fe3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;2c&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Int32&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0 instance&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 _controlState&lt;br&gt;68a85ea0&amp;nbsp; 4001fe4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 10&amp;nbsp;&amp;nbsp; ...m.Web.UI.StateBag&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 instance 00000000 _viewState&lt;br&gt;68a2af44&amp;nbsp; 4001fe5&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 14&amp;nbsp;&amp;nbsp; ...em.Web.UI.Control&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 instance 023e8864 _namingContainer&lt;br&gt;68a273d0&amp;nbsp; 4001fe6&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 18&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.Web.UI.Page&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0 instance 01df4514 _page&lt;br&gt;68a92e2c&amp;nbsp; 4001fe7&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1c&amp;nbsp;&amp;nbsp; ...+OccasionalFields&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 instance 00000000 _occasionalFields&lt;br&gt;68a2b378&amp;nbsp; 4001fe8&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 20&amp;nbsp;&amp;nbsp; ...I.TemplateControl&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 instance 00000000 _templateControl&lt;br&gt;68a14528&amp;nbsp; 4001fe9&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;24&amp;nbsp;&amp;nbsp; ...m.Web.VirtualPath&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 instance 00000000 _templateSourceVirtualDirectory&lt;br&gt;68a8bb48&amp;nbsp; 4001fea&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;28&amp;nbsp;&amp;nbsp; ...rs.ControlAdapter&amp;nbsp;&amp;nbsp;&amp;nbsp; 0 instance 00000000 _adapter&lt;br&gt;68a3a8f8&amp;nbsp; 4001feb&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;30&amp;nbsp;&amp;nbsp; ...SimpleBitVector32&amp;nbsp;&amp;nbsp;&amp;nbsp; 1 instance 023ea0d8 flags&lt;br&gt;790f9c18&amp;nbsp; 4001fda&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;c70&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Object&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp;&amp;nbsp;shared&amp;nbsp;&amp;nbsp;&amp;nbsp;static EventDataBinding&lt;br&gt;&amp;gt;&amp;gt; Domain:Value 000f0d00:NotInit 0011a720:01df0028 &amp;lt;&amp;lt;&lt;br&gt;790f9c18&amp;nbsp; 4001fdb&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;c74&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Object&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp; shared&amp;nbsp;&amp;nbsp;&amp;nbsp;static EventInit&lt;br&gt;&amp;gt;&amp;gt; Domain:Value 000f0d00:NotInit 0011a720:01df0034 &amp;lt;&amp;lt;&lt;br&gt;790f9c18&amp;nbsp; 4001fdc&amp;nbsp;&amp;nbsp;&amp;nbsp; c78&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Object&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp; shared&amp;nbsp;&amp;nbsp;&amp;nbsp;static EventLoad&lt;br&gt;&amp;gt;&amp;gt; Domain:Value 000f0d00:NotInit 0011a720:01df0040 &amp;lt;&amp;lt;&lt;br&gt;790f9c18&amp;nbsp; 4001fdd&amp;nbsp;&amp;nbsp;&amp;nbsp; c7c&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Object&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp; shared&amp;nbsp;&amp;nbsp; static EventUnload&lt;br&gt;&amp;gt;&amp;gt; Domain:Value 000f0d00:NotInit 0011a720:01df004c &amp;lt;&amp;lt;&lt;br&gt;790f9c18&amp;nbsp; 4001fde&amp;nbsp;&amp;nbsp;&amp;nbsp; c80&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.Object&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp; shared&amp;nbsp;&amp;nbsp; static EventPreRender&lt;br&gt;&amp;gt;&amp;gt; Domain:Value 000f0d00:NotInit 0011a720:01df0058 &amp;lt;&amp;lt;&lt;br&gt;790f9c18&amp;nbsp; 4001fdf&amp;nbsp;&amp;nbsp;&amp;nbsp; c84&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.Object&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp; shared&amp;nbsp;&amp;nbsp; static EventDisposed&lt;br&gt;&amp;gt;&amp;gt; Domain:Value 000f0d00:NotInit 0011a720:01df0064 &amp;lt;&amp;lt;&lt;br&gt;79124228&amp;nbsp; 4001fec&amp;nbsp;&amp;nbsp;&amp;nbsp; c88&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.Object[]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0&amp;nbsp;&amp;nbsp; shared&amp;nbsp;&amp;nbsp; static automaticIDs&lt;br&gt;&amp;gt;&amp;gt; Domain:Value 000f0d00:NotInit 0011a720:01df0070 &amp;lt;&amp;lt;&lt;br&gt;790fa3e0&amp;nbsp; 4002211&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 34&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.String&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0 instance 02238664 _text &lt;/div&gt; &lt;p&gt;Cool, now we can take a look at the specifics. For example the value of the text-property, which is located in the string down at the bottom with address 02238664. To get it's value, simply perform a &lt;span class="InlineCode"&gt;!do&lt;/span&gt; on the address:  &lt;div class="SampleCode"&gt;0:000&amp;gt; !do 02238664 &lt;br&gt;Name: System.String&lt;br&gt;MethodTable: 790fa3e0&lt;br&gt;EEClass: 790fa340&lt;br&gt;Size: 158(0x9e) bytes&lt;br&gt;GC Generation: 2&lt;br&gt;(C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)&lt;br&gt;String:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/td&amp;gt;&lt;br&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;lt;/tr&amp;gt;&lt;br&gt;&amp;nbsp; &amp;lt;/table&amp;gt;&lt;br&gt;&amp;lt;!-- end&amp;nbsp;of&amp;nbsp;content table&amp;nbsp;--&amp;gt; &lt;br&gt;&amp;nbsp;&lt;br&gt;Fields:&lt;br&gt;MT Field Offset Type VT Attr Value Name&lt;br&gt;790fed1c 4000096 4 System.Int32 0 instance 71 m_arrayLength&lt;br&gt;790fed1c 4000097 8 System.Int32 0 instance 70 m_stringLength&lt;br&gt;790fbefc 4000098 c System.Char 0 instance 3c m_firstChar&lt;br&gt;790fa3e0 4000099 10 System.String 0 shared static Empty&lt;br&gt;&amp;gt;&amp;gt; Domain:Value 000f0d00:790d6584 0011a720:790d6584 &amp;lt;&amp;lt;&lt;br&gt;79124670 400009a 14 System.Char[] 0 shared static WhitespaceChars&lt;br&gt;&amp;gt;&amp;gt; Domain:Value 000f0d00:01d413b8 0011a720:01d44f80 &amp;lt;&amp;lt; &lt;/div&gt; &lt;p&gt;Okay, so we can see that the string contains some data to close off a table.&amp;nbsp;We can also take a look at the other properties for the object and examine them if we wish. But there is another command that is really useful...  &lt;h2&gt;Using !objsize&lt;/h2&gt; &lt;p&gt;Is there a way to get the total size of a specific System.Web.UI.LiteralControl? - Simple! We use the &lt;span class="InlineCode"&gt;!objsize &lt;/span&gt;-command. !objsize looks at all the pointers within an object and calculate their total size. Below is the built in documentation for the !objsize -command:  &lt;div class="SampleCode"&gt;0:000&amp;gt; !help objsize&lt;br&gt;-------------------------------------------------------------------------------&lt;br&gt;!ObjSize [&amp;lt;Object address&amp;gt;] &lt;br&gt;&amp;nbsp;&lt;br&gt;With no parameters, !ObjSize lists the size of all objects found on managed &lt;br&gt;threads. It also enumerates all GCHandles in the process, and totals the size &lt;br&gt;of any objects pointed to by those handles. In calculating object size, &lt;br&gt;!ObjSize includes the size of all child objects in addition to the parent. &lt;br&gt;&amp;nbsp;&lt;br&gt;For example, !DumpObj lists a size of 20 bytes for this Customer object: &lt;br&gt;&amp;nbsp;&lt;br&gt;0:000&amp;gt; !do a79d40&lt;br&gt;Name: Customer&lt;br&gt;MethodTable: 009038ec&lt;br&gt;EEClass: 03ee1b84&lt;br&gt;Size: 20(0x14) bytes&lt;br&gt;(C:\pub\unittest.exe)&lt;br&gt;Fields:&lt;br&gt;MT&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Field Offset&amp;nbsp; Type&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; Attr&amp;nbsp;&amp;nbsp;&amp;nbsp; Value&amp;nbsp;&amp;nbsp;Name&lt;br&gt;009038ec 4000008&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4 CLASS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; instance 00a79ce4&amp;nbsp;&amp;nbsp;name&lt;br&gt;009038ec 4000009&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 8 CLASS&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; instance 00a79d2c&amp;nbsp; bank&lt;br&gt;009038ec 400000a&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; c System.Boolean instance&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1 valid &lt;br&gt;&amp;nbsp;&lt;br&gt;but !ObjSize lists 152 bytes:&lt;br&gt;0:000&amp;gt; !ObjSize a79d40&lt;br&gt;sizeof(00a79d40) = 152 ( 0x98) bytes (Customer) &lt;br&gt;&amp;nbsp;&lt;br&gt;This is because a Customer points to a Bank, has a name, and the Bank points to&lt;br&gt;an Address string. You can use !ObjSize to identify any particularly large &lt;br&gt;objects, such as a managed cache in a web server. &lt;/div&gt; &lt;h2&gt;&amp;nbsp;&lt;/h2&gt; &lt;h2&gt;Objects in heap may also be &lt;em&gt;smaller &lt;/em&gt;than they appear&lt;/h2&gt; &lt;p&gt;So what do we get if we run !objsize on our LiteralControl? This is really interesting, because what happens is; the debugger gets really busy for quite some time and eventually we get this:&lt;/p&gt; &lt;div class="SampleCode"&gt;0:000&amp;gt; !objsize 023ea0a8 &lt;br&gt;sizeof(023ea0a8) = 456918136 ( 0x1b3c0478) bytes (System.Web.UI.LiteralControl)&lt;/div&gt; &lt;p&gt;456 MBytes! How is that possible? Well&amp;nbsp;if you scroll up to where we&amp;nbsp;ran the !do command on the&amp;nbsp;LiteralControl, you'll see that the control holds a reference to the page. The&amp;nbsp;page in turn has a reference to the cache, and before long we'll have&amp;nbsp;referenced almost the entire heap.&lt;/p&gt; &lt;h1&gt;Summary&lt;/h1&gt; &lt;p&gt;Hopefully this is enough to give you a quick glimpse of what is possible with three relatively simple commands from the sos extension. The commands were:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;!dumpheap  &lt;li&gt;!dumpobj  &lt;li&gt;!objsize&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;Over and out&lt;/p&gt; &lt;p&gt;/ Johan&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=1451030" width="1" height="1"&gt;</description><category domain="http://blogs.msdn.com/johan/archive/tags/OutOfMemory/default.aspx">OutOfMemory</category><category domain="http://blogs.msdn.com/johan/archive/tags/WinDbg/default.aspx">WinDbg</category><category domain="http://blogs.msdn.com/johan/archive/tags/GC/default.aspx">GC</category></item></channel></rss>