<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Mingbo &#187; dot net</title>
	<atom:link href="http://shao.mingbo.de/tag/dot-net/feed/" rel="self" type="application/rss+xml" />
	<link>http://shao.mingbo.de</link>
	<description>包括教育技术，编程，互联网等方面的文章及随想。</description>
	<lastBuildDate>Thu, 26 Aug 2010 02:57:15 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=abc</generator>
		<item>
		<title>.net垃圾回收的剖析</title>
		<link>http://shao.mingbo.de/2009/11/23/dot-net-garbage-collector-analysis/</link>
		<comments>http://shao.mingbo.de/2009/11/23/dot-net-garbage-collector-analysis/#comments</comments>
		<pubDate>Mon, 23 Nov 2009 09:37:54 +0000</pubDate>
		<dc:creator>邵 明博</dc:creator>
				<category><![CDATA[.NET 编程]]></category>
		<category><![CDATA[csharp]]></category>
		<category><![CDATA[dot net]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[查缺补漏]]></category>

		<guid isPermaLink="false">http://shao.mingbo.de/?p=218</guid>
		<description><![CDATA[看了好几本.net专著，每一本都会提到.net的垃圾回收机制，而且，每一本都会不约而同的告诉你:&#8221;垃圾回收的一个重要方面是它的不确定性。换而言之,不能保证什么时候会调用垃圾收集器&#8230;&#8221;。看到这里，你会很自然的对这个“不确定性”感兴趣——既然不确定，.net又是怎么保证垃圾在很大程度上被回收的呢？这个垃圾回收机制能否有一个更加完善的版本呢？ 在.net之前,windows平台已经用了2种技术来释放进程向系统动态请求的内存： 完全手工的方式。 让对象维护引用计数。 完全手工的方式的一个经典例子，便是大名鼎鼎的c++.它能够很有效的释放内存，但缺点是容易频繁的出错，内存泄露时有发生。尽管现代的开发环境提供了内存泄露检测工具，但它们很难跟踪错误，因为直到内存已大量泄露使得windows拒绝服务时，它们才能发挥作用；让对象维护引用计数，是COM对象采用的一种技术。每个COM对象都保留一个客户机对自己的引用计数。当这个计数下降到0的时候，组件就会自己删除自己，并释放相应的内存和资源。但，如果客户机没有通知成功，那么COM对象便一直停留在内存。这一点比起C++内存泄露更为可怕。因为，一旦C++的进程被迫结束时也会释放相应的内存和资源，而COM对象占用的内存可能一直都不会被系统删除，直到你重新启动。 .net采用的垃圾回收就很好的规避了上述的缺点。 和所有语言一样，所有动态请求的内存都会分布在堆上，而.net是放到自己的CLR托管堆上。为了便于理解，我们先讨论一个简化的垃圾回收模型： 这个模型遵循以下规则： 所有的对象都分配到一个连续的内存空间。 堆被分割成了若干个等级，以便于我们能够只观察某一等级的堆，就能最大可能的清除最多的垃圾。 存储在同一等级的堆中的对象，大致上具有相同的生命周期。 等级越高的堆中，所存放的对象越稳定。 周期性的移动对象，以扩展未使用区域的内存。 对象在内存的顺序与创建的它们的顺序是相同的。 这里需要解释的是：.net垃圾回收器将堆分为了3个等级（generation0,1,2），对应的托管堆的初始化大小是256k,2M,10M。最近被分配的内存空间的对象被放置在第0代，一般存储于二级缓存中，所以能对第0代中的对象实现快速存取。经过一轮垃圾回收后，仍然保留在第0代中的对象则被移进第1代中，再经过一轮垃圾回收后，仍然保留再第1代中的对象则被移进第2代中，第2代中包含了生存期较长的对象，这些对象至少经过了两轮回收。 从这样等级制度中，我们不难给出下面的假设： 生命周期越长的对象需要释放的可能性越小, 通过几轮垃圾回收后,它们被存放到了Gen 2里，相比起刚分配内存的那些对象而言，G2的对象不太可能是下一个被释放的人选。因此，大多数情况下，我们收集垃圾时，忽略G2的内存堆，而搜索最近被分配的对象集合有助于花费最少的工作来释放尽可能多的空闲内存空间。 当然，假设永远是一个假设。我们不能保证G2中没有对象不需要释放的。因此.net回收机制设置了一个card table&#38;wrtie barrier的算法来尽力的保证回收的有效性。CT你可以理解为一段连续的内存，比如128byte.每当程序需要写入一块内存时，他会通过WB计算哪一块可以被写,就可以发现被修改了的G2对象，然后在ct中进行设置。通过这样的机制，当我们处理完等级为0的堆后，就可以在CT中寻找已经被修改了的对象。从而有效的完成垃圾回收。 但，事无完美，一旦CT内存被大量的G2对象占满时，则上述机制运行无效。但绝大多数情况下，G2对象是稳定的。这样，也能够解释，大多数的.net书籍会那么介绍垃圾回收机制了。当然是否有更好的机制，那得看聪明的您了。 参考资料： 《Professional C# 2008》Christian Nagel，Bill Evjen@  Wiley Publishing, Inc. MSDN doc:  相关文章2009年11月25日 -- cSharp编程基础的学习笔记2009年12月22日 -- cSharp中BackgroundWorker的用法2009年12月21日 -- cSharp中同步类的设计2009年12月20日 -- cSharp中给线程传递参数2009年12月19日 -- cSharp中异步委托的笔记]]></description>
			<content:encoded><![CDATA[<p style="text-align: left;">看了好几本.net专著，每一本都会提到<strong>.net的垃圾回收</strong>机制，而且，每一本都会不约而同的告诉你:&#8221;垃圾回收的一个重要方面是它的不确定性。换而言之,不能保证什么时候会调用垃圾收集器&#8230;&#8221;。看到这里，你会很自然的对这个“不确定性”感兴趣——既然不确定，.net又是怎么保证垃圾在很大程度上被回收的呢？这个垃圾回收机制能否有一个更加完善的版本呢？</p>
<p style="text-align: left;">在.net之前,windows平台已经用了2种技术来释放进程向系统动态请求的内存：</p>
<ul style="text-align: left;">
<li>完全手工的方式。</li>
<li>让对象维护引用计数。</li>
</ul>
<p style="text-align: left;">完全手工的方式的一个经典例子，便是大名鼎鼎的c++.它能够很有效的释放内存，但缺点是容易频繁的出错，内存泄露时有发生。尽管现代的开发环境提供了内存泄露检测工具，但它们很难跟踪错误，因为直到内存已大量泄露使得windows拒绝服务时，它们才能发挥作用；让对象维护引用计数，是COM对象采用的一种技术。每个COM对象都保留一个客户机对自己的引用计数。当这个计数下降到0的时候，组件就会自己删除自己，并释放相应的内存和资源。但，如果客户机没有通知成功，那么COM对象便一直停留在内存。这一点比起C++内存泄露更为可怕。因为，一旦C++的进程被迫结束时也会释放相应的内存和资源，而COM对象占用的内存可能一直都不会被系统删除，直到你重新启动。</p>
<p style="text-align: left;">.net采用的垃圾回收就很好的规避了上述的缺点。</p>
<p style="text-align: left;">和所有语言一样，所有动态请求的内存都会分布在堆上，而.net是放到自己的CLR托管堆上。为了便于理解，我们先讨论一个简化的垃圾回收模型：</p>
<p style="text-align: center;"><a href="http://shao.mingbo.de/wp-content/uploads/2009/11/Garbage-Collector-Basics-and-Performance-Hints.bmp"><img class="size-full wp-image-222 alignnone" title="Garbage-Collector-Basics-and-Performance-Hints" src="http://shao.mingbo.de/wp-content/uploads/2009/11/Garbage-Collector-Basics-and-Performance-Hints.bmp" alt="Garbage Collector Basics and Performance Hints .net垃圾回收的剖析"  /></a></p>
<p style="text-align: left;">这个模型遵循以下规则：</p>
<ul style="text-align: center;">
<li style="text-align: left;">所有的对象都分配到一个连续的内存空间。</li>
<li style="text-align: left;">堆被分割成了若干个等级，以便于我们能够只观察某一等级的堆，就能最大可能的清除最多的垃圾。</li>
<li style="text-align: left;"><span style="color: #ff0000;">存储在同一等级的堆中的对象，大致上具有相同的生命周期。</span></li>
<li style="text-align: left;">等级越高的堆中，所存放的对象越稳定。</li>
<li style="text-align: left;">周期性的移动对象，以扩展未使用区域的内存。</li>
<li style="text-align: left;">对象在内存的顺序与创建的它们的顺序是相同的。</li>
</ul>
<p>这里需要解释的是：<strong>.net垃圾回收器</strong>将堆分为了3个等级（generation0,1,2），对应的托管堆的初始化大小是256k,2M,10M。最近被分配的内存空间的对象被放置在第0代，一般存储于二级缓存中，所以能对第0代中的对象实现快速存取。经过一轮垃圾回收后，仍然保留在第0代中的对象则被移进第1代中，再经过一轮垃圾回收后，仍然保留再第1代中的对象则被移进第2代中，第2代中包含了生存期较长的对象，这些对象至少经过了两轮回收。</p>
<p>从这样等级制度中，我们不难给出下面的假设：</p>
<p>生命周期越长的对象需要释放的可能性越小, 通过几轮垃圾回收后,它们被存放到了Gen 2里，相比起刚分配内存的那些对象而言，G2的对象不太可能是下一个被释放的人选。因此，大多数情况下，我们收集垃圾时，忽略G2的内存堆，而搜索最近被分配的对象集合有助于花费最少的工作来释放尽可能多的空闲内存空间。</p>
<p>当然，假设永远是一个假设。我们不能保证G2中没有对象不需要释放的。因此.net回收机制设置了一个card table&amp;wrtie barrier的算法来尽力的保证回收的有效性。CT你可以理解为一段连续的内存，比如128byte.每当程序需要写入一块内存时，他会通过WB计算哪一块可以被写,就可以发现被修改了的G2对象，然后在ct中进行设置。通过这样的机制，当我们处理完等级为0的堆后，就可以在CT中寻找已经被修改了的对象。从而有效的完成垃圾回收。</p>
<p>但，事无完美，一旦CT内存被大量的G2对象占满时，则上述机制运行无效。但绝大多数情况下，G2对象是稳定的。这样，也能够解释，大多数的.net书籍会那么介绍<strong>垃圾回收机制</strong>了。当然是否有更好的机制，那得看聪明的您了。</p>
<p>参考资料：</p>
<ul>
<li>《Professional C# 2008》Christian Nagel，Bill Evjen@  Wiley Publishing, Inc.</li>
<li>MSDN doc: <a class="downloadlink" href="http://shao.mingbo.de/wp-content/plugins/download-monitor/download.php?id=3" title=" downloaded 46 times" >Garbage Collector Basics and Performance Hints (46)</a></li>
</ul>
<h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年11月25日 -- <a href="http://shao.mingbo.de/2009/11/25/basical-knowledge-of-csharp/" title="cSharp编程基础的学习笔记">cSharp编程基础的学习笔记</a></li><li>2009年12月22日 -- <a href="http://shao.mingbo.de/2009/12/22/how-to-use-backgroundworker-in-csharp/" title="cSharp中BackgroundWorker的用法">cSharp中BackgroundWorker的用法</a></li><li>2009年12月21日 -- <a href="http://shao.mingbo.de/2009/12/21/synchronize-class-design-in-csharp/" title="cSharp中同步类的设计">cSharp中同步类的设计</a></li><li>2009年12月20日 -- <a href="http://shao.mingbo.de/2009/12/20/paralize-thread-in-charp/" title="cSharp中给线程传递参数">cSharp中给线程传递参数</a></li><li>2009年12月19日 -- <a href="http://shao.mingbo.de/2009/12/19/asynchronous-delegate-in-csharp/" title="cSharp中异步委托的笔记">cSharp中异步委托的笔记</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://shao.mingbo.de/2009/11/23/dot-net-garbage-collector-analysis/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
