积极答复者
关于Dispose方法使用的问题

问题
-
为了增加软件释放内存的功能,我查了msdn,使用Dispose方法
由于是初学者,按msdn的代码复制到工程中,发现其中两个定义对象或变量的语句出错,不知道该引用哪个命名空间,请大家帮忙一下
定义对象或变量的语句如下:
// A managed resource that you add in this derived class.
private ManagedResource addedManaged;
// A native unmanaged resource that you add in this derived class.
private NativeResource addedNative;
田田qq:764574267
答案
-
这个问题出现的很正常。因为一旦将 WebBrowser Dispose,那么它就放到待回收队列去了,此时如果再次访问它,CLR 会报一个无法访问已经 Dispose 资源的 Exception。
当然,Dispose 后有些方法是可以再次访问该对象的,这个就是神奇的对象复活。。。我就不多说了。
WebBrowser 实际上是 Internet Explorer ActiveX 的一个 Wrapper,用的是 shdocvw.dll。shdocvw.dll 是非托管的。在只有一个 WebBrowser 实例的 Windows Forms 上,Form 的 Dispose 会自动处理 WebBrowser 的 Dispose。不过,如果在一个 Form 上放了多个 WebBrowser (包括动态创建的),这就可能造成内存泄漏。因为非托管资源的回收需要显式调用 Dispose(false)。而这个工作是在 Form 的 Finalizer 完成的。
一个经典的,也是最好的 Dispose 实现如下:
public class Form : IDisposable
{
public void Dispose()
{
this.Dispose(true);
System.GC.SupressFinalizer(this);
}
public virtual void Dispose(bool disposing)
{
if (disposing) // 托管资源
{
} else {
// 非托管资源
}
}
~Form()
{
this.Dispose(false);
}
}可见,非托管资源在窗体的析构函数 (Finalizer) 中被回收。而 Form 的析构函数在窗体/应用程序被关闭时执行。
我只能解释原理,剩下的如何寻找解决方案,就要看您怎么修改实现了。
Mark Zhou- 已标记为答案 KeFang Chen 2010年5月17日 6:32
全部回复
-
这个问题出现的很正常。因为一旦将 WebBrowser Dispose,那么它就放到待回收队列去了,此时如果再次访问它,CLR 会报一个无法访问已经 Dispose 资源的 Exception。
当然,Dispose 后有些方法是可以再次访问该对象的,这个就是神奇的对象复活。。。我就不多说了。
WebBrowser 实际上是 Internet Explorer ActiveX 的一个 Wrapper,用的是 shdocvw.dll。shdocvw.dll 是非托管的。在只有一个 WebBrowser 实例的 Windows Forms 上,Form 的 Dispose 会自动处理 WebBrowser 的 Dispose。不过,如果在一个 Form 上放了多个 WebBrowser (包括动态创建的),这就可能造成内存泄漏。因为非托管资源的回收需要显式调用 Dispose(false)。而这个工作是在 Form 的 Finalizer 完成的。
一个经典的,也是最好的 Dispose 实现如下:
public class Form : IDisposable
{
public void Dispose()
{
this.Dispose(true);
System.GC.SupressFinalizer(this);
}
public virtual void Dispose(bool disposing)
{
if (disposing) // 托管资源
{
} else {
// 非托管资源
}
}
~Form()
{
this.Dispose(false);
}
}可见,非托管资源在窗体的析构函数 (Finalizer) 中被回收。而 Form 的析构函数在窗体/应用程序被关闭时执行。
我只能解释原理,剩下的如何寻找解决方案,就要看您怎么修改实现了。
Mark Zhou- 已标记为答案 KeFang Chen 2010年5月17日 6:32
-
mazhou:您好
"可见,非托管资源在窗体的析构函数 (Finalizer) 中被回收。而 Form 的析构函数在窗体/应用程序被关闭时执行。"
我的这个应用程序运行后不关闭,让它一直运行,有两个WebBrower实例,其中一个实例只打开一次网页就不管它了,另一个实例我会定时(现在是5秒)打开一个不同的网页,但随着时间的推移,这个程序占用的内存越来越大,不知道是不是内存泄漏,如果是,应该如果处理?如果要用dispose进行回收,看了你上面的经典例子后,好像是在窗体关闭或程序退出时进行的,但这个程序就这一个窗体,不关闭它,也不退出程序,像这种情况应该如何减少内存占用?
田田qq:764574267 -
其实这里一定存在内存泄漏的,WebBrowser 的行为跟 IE 是保持一致的。利用 GC.Collect() 或者 Dispose 只能回收部分托管资源。对于 IE 泄露的内存则可能无能为力。
如果窗体一直打开运行,且只有一个 WebBrowser 控件的话,也没什么好优化的了。这里内存泄露的原因主要在于访问的 Web 页。
您可以做一个简单的试验。打开一 IE,依次输入 10 个 URL 访问 10 个不同的 Web 页,用 Performance Counter 或者 Task Manager 记下每一个 Page 打开后的内存情况。您或许会发现,您的内存使用有增无减。还有,有一些 Web 页使用 AJAX/JavaScript/Timer 获取数据,如果程序代码写得不当,则可能造成更多的内存泄露。
IE 的内存泄露主要表现在:
- JavaScript 闭包导致泄露
- 不恰当的 JavaScript 编码方式导致泄露
- InnerHTML, AppendChild 的内存泄露
- 浏览器后退/前进按钮导致的内存泄露其实这个问题已经与 Dispose 无关了。要找到 Workaround 的话,您可以先按照我说的做个实验,确保不是由于不同的 Web 页访问导致内存泄露的。
Mark Zhou