<?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>简单生活 -- Kevin Yang的博客 &#187; 内存泄露</title>
	<atom:link href="http://www.imkevinyang.com/tags/%e5%86%85%e5%ad%98%e6%b3%84%e9%9c%b2/feed" rel="self" type="application/rss+xml" />
	<link>http://www.imkevinyang.com</link>
	<description>It&#039;s all about sharing</description>
	<lastBuildDate>Sun, 05 Feb 2012 15:37:14 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>DotNet中未退订事件引起的内存泄露问题</title>
		<link>http://www.imkevinyang.com/2009/11/dotnet%e4%b8%ad%e6%9c%aa%e9%80%80%e8%ae%a2%e4%ba%8b%e4%bb%b6%e5%bc%95%e8%b5%b7%e7%9a%84%e5%86%85%e5%ad%98%e6%b3%84%e9%9c%b2%e9%97%ae%e9%a2%98.html</link>
		<comments>http://www.imkevinyang.com/2009/11/dotnet%e4%b8%ad%e6%9c%aa%e9%80%80%e8%ae%a2%e4%ba%8b%e4%bb%b6%e5%bc%95%e8%b5%b7%e7%9a%84%e5%86%85%e5%ad%98%e6%b3%84%e9%9c%b2%e9%97%ae%e9%a2%98.html#comments</comments>
		<pubDate>Thu, 12 Nov 2009 06:04:00 +0000</pubDate>
		<dc:creator>Kevin Yang</dc:creator>
				<category><![CDATA[其他随笔]]></category>
		<category><![CDATA[DotNet]]></category>
		<category><![CDATA[事件]]></category>
		<category><![CDATA[内存]]></category>
		<category><![CDATA[内存泄露]]></category>
		<category><![CDATA[委托]]></category>

		<guid isPermaLink="false">http://www.imkevinyang.com/2009/11/dotnet%e4%b8%ad%e6%9c%aa%e9%80%80%e8%ae%a2%e4%ba%8b%e4%bb%b6%e5%bc%95%e8%b5%b7%e7%9a%84%e5%86%85%e5%ad%98%e6%b3%84%e9%9c%b2%e9%97%ae%e9%a2%98.html</guid>
		<description><![CDATA[<p>今天在InfoQ上看到一篇文章<a title="处理.NET中的内存泄露" href="http://www.infoq.com/cn/news/2009/11/Memory-Leaks-.NET" target="_blank">处理.NET中的内存泄露</a>，里头提到说DotNet内存泄露的几种常见情况，</p>
<ul>
<li>使用静态引用 <strong><font color="#008000"></font></strong></li>
<li>未退订的事件－作者认为这是最常见的内存泄漏原因 </li>
<li>未退订的静态事件 </li>
<li>未调用Dispose方法 </li>
<li>使用不彻底的Dispose方法 </li>
<li>在Windows Forms中对BindingSource的误用 </li>
<li>未在Wo&#8230;</li></ul>]]></description>
			<content:encoded><![CDATA[<p>今天在InfoQ上看到一篇文章<a title="处理.NET中的内存泄露" href="http://www.infoq.com/cn/news/2009/11/Memory-Leaks-.NET" target="_blank">处理.NET中的内存泄露</a>，里头提到说DotNet内存泄露的几种常见情况，</p>
<ul>
<li>使用静态引用 <strong><font color="#008000"></font></strong></li>
<li>未退订的事件－作者认为这是最常见的内存泄漏原因 </li>
<li>未退订的静态事件 </li>
<li>未调用Dispose方法 </li>
<li>使用不彻底的Dispose方法 </li>
<li>在Windows Forms中对BindingSource的误用 </li>
<li>未在WorkItem/CAB上调用Remove </li>
</ul>
<p>当时对于第二点，“未退订事件”引起的内存泄露不是很理解。后来仔细分析了一下，又参考了MSDN上关于事件和委托的解释，终于意识到了自己以前没有注意的一个问题。</p>
<blockquote><p>假设，类A提供了一个事件，类B和类C均订阅了该事件。如果类B和类C的实例不再使用了，而又没有显式退订此事件，那么类B和类C的实例将不会被GC回收，直至类A实例被销毁为止。</p>
</blockquote>
<p>下面的代码说明了这种情况：</p>
<pre class="brush: csharp">public class Container
{
	private List&lt;Containee&gt; _controls = new List&lt;Containee&gt;();

	public List&lt;Containee&gt; Controls
	{
		get { return _controls; }
		set { _controls = value; }
	}
	private String _currentState;

	public event EventHandler StateChanged;

	public String CurrentState
	{
		get
		{
			return _currentState;
		}
		set
		{
			if (value != _currentState)
			{
				_currentState = value;
				if (StateChanged != null)
				{
					StateChanged(this, null);
				}
			}
		}
	}

	public void ClearControls()
	{
		_controls.Clear();
	}
}

public class Containee
{
	private String _name;
	private Container _container;
	public Containee(String controlName, Container container)
	{
		_name = controlName;
		_container = container;
		_container.Controls.Add(this);
		container.StateChanged += OnStateChanged;
	}

	public void OnStateChanged(object sender, EventArgs e)
	{
		Container container = sender as Container;
		if (container != null)
		{
			// Do something....
			Console.WriteLine(&quot;{0}: State change, current state is {1}.&quot;, _name, container.CurrentState);
		}
	}
}
class Program
{
	static void Main(string[] args)
	{
		Container container = new Container();
		new Containee(&quot;Control 1&quot;, container);
		new Containee(&quot;Control 2&quot;, container);
		new Containee(&quot;Control 3&quot;, container);
		container.CurrentState = &quot;State1&quot;;

		Console.WriteLine(&quot;================================================&quot;);
		// 清除所有子控件
		container.ClearControls();
		// 添加新控件进来
		new Containee(&quot;Control 4&quot;, container);
		new Containee(&quot;Control 5&quot;, container);
		new Containee(&quot;Control 6&quot;, container);
		container.CurrentState = &quot;State2&quot;;
	}
}</pre>
<p>运行结果：</p>
<p><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://www.imkevinyang.com/wp-content/uploads/2009/11/image_thumb8.png" width="387" height="159" /></p>
<p>如上述结果看到的，尽管旧控件已经被clear掉了，但是它们仍在起作用。</p>
<p>当我们在后面代码中将container容器上的控件清除掉的时候，我们会习惯性的以为这些控件已经不再被引用了，资源会被自动回收掉。而设计Containee的人可能在写事件订阅的时候也不会想起背后的逻辑，习以为常的觉得，只要在代码里找到该Containee实例都被“=”给了谁，然后把它的引用置为null就行了。殊不知，C#的event-delegate模型下，当一个类实例订阅了一个事件的时候，此实例已经被隐式的传给了“事件”了（CLR的事件模型是建立在委托System.Delegate之上的，事件可以看成是一个委托类）。因此，只要Container不销毁，事件也就不销毁，Containee的实例就不会被回收，除非Containee退订了该事件。</p>
<p>自己在写事件处理的时候根本没有考虑过这样的问题，不知道以前写过的代码会不会也存在这样的问题。不管怎么，以后得注意一些了。</p>
<p style="text-align: right">——<a title="DotNet中未退订事件引起的内存泄露问题" href="http://www.imkevinyang.com/2009/11/dotnet%E4%B8%AD%E6%9C%AA%E9%80%80%E8%AE%A2%E4%BA%8B%E4%BB%B6%E5%BC%95%E8%B5%B7%E7%9A%84%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E9%97%AE%E9%A2%98.html"><em>Kevin Yang</em></a></p>

	标签：<a href="http://www.imkevinyang.com/tags/dotnet" title="DotNet" rel="tag">DotNet</a>, <a href="http://www.imkevinyang.com/tags/%e4%ba%8b%e4%bb%b6" title="事件" rel="tag">事件</a>, <a href="http://www.imkevinyang.com/categories/techarticles/othertecharticles" title="其他随笔" rel="tag">其他随笔</a>, <a href="http://www.imkevinyang.com/tags/%e5%86%85%e5%ad%98" title="内存" rel="tag">内存</a>, <a href="http://www.imkevinyang.com/tags/%e5%86%85%e5%ad%98%e6%b3%84%e9%9c%b2" title="内存泄露" rel="tag">内存泄露</a>, <a href="http://www.imkevinyang.com/tags/%e5%a7%94%e6%89%98" title="委托" rel="tag">委托</a><br />

	<h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.imkevinyang.com/2009/09/%e9%87%8a%e6%94%besql-server%e5%8d%a0%e7%94%a8%e7%9a%84%e5%86%85%e5%ad%98.html" title="释放SQL Server占用的内存 (2009/09/01)">释放SQL Server占用的内存</a> </li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.imkevinyang.com/2009/11/dotnet%e4%b8%ad%e6%9c%aa%e9%80%80%e8%ae%a2%e4%ba%8b%e4%bb%b6%e5%bc%95%e8%b5%b7%e7%9a%84%e5%86%85%e5%ad%98%e6%b3%84%e9%9c%b2%e9%97%ae%e9%a2%98.html/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using disk: enhanced

Served from: www.imkevinyang.com @ 2012-02-09 10:28:13 -->
