我通过阅读微软文档知道,IDisposable
接口的"主要"用途是清理未管理的资源。
对我来说,"非管理 "是指像数据库连接、套接字、窗口手柄等。 但是,我看到代码中的Dispose()
方法是用来释放_managed_资源的,这对我来说是多余的,因为垃圾收集器应该为你处理这些。
比如说。
public class MyCollection : IDisposable
{
private List<String> _theList = new List<String>();
private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();
// Die, clear it up! (free unmanaged resources)
public void Dispose()
{
_theList.clear();
_theDict.clear();
_theList = null;
_theDict = null;
}
我的问题是,这是否会使垃圾收集器释放MyCollection
所使用的内存的速度比正常情况下要快?
edit: 到目前为止,人们已经发布了一些使用IDisposable来清理非管理资源的好例子,比如数据库连接和位图。 但是假设上面的代码中的_theList
包含了一百万个字符串,你想现在就释放这些内存,而不是等待垃圾收集器的到来。 上面的代码能做到这一点吗?
IDisposable
经常被用来利用using
语句,并利用一种简单的方式对被管理对象进行确定性清理。
public class LoggingContext : IDisposable {
public Finicky(string name) {
Log.Write("Entering Log Context {0}", name);
Log.Indent();
}
public void Dispose() {
Log.Outdent();
}
public static void Main() {
Log.Write("Some initial stuff.");
try {
using(new LoggingContext()) {
Log.Write("Some stuff inside the context.");
throw new Exception();
}
} catch {
Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
} finally {
Log.Write("Some final stuff.");
}
}
}
是的,这些代码完全是多余的和不必要的,它并没有让垃圾收集器做任何它不会做的事情(一旦MyCollection的一个实例超出了范围,就是这样),特别是.Clear()
的调用。
对你的编辑的回答:算是吧。 如果我这样做。
public void WasteMemory()
{
var instance = new MyCollection(); // this one has no Dispose() method
instance.FillItWithAMillionStrings();
}
// 1 million strings are in memory, but marked for reclamation by the GC
就内存管理而言,它在功能上与此相同。
public void WasteMemory()
{
var instance = new MyCollection(); // this one has your Dispose()
instance.FillItWithAMillionStrings();
instance.Dispose();
}
// 1 million strings are in memory, but marked for reclamation by the GC
如果你真的非常需要在这个时候释放内存,可以调用GC.Collect()
。 不过,没有理由在这里这么做。 内存将在需要时被释放。
如果MyCollection
无论如何都会被垃圾回收,那么你就不需要处置它。 这样做只会使CPU的负荷超过需要,甚至可能使垃圾收集器已经进行的一些预先计算的分析失效。
我使用 "IDisposable "来做一些事情,比如确保线程被正确处置,以及未管理的资源。
EDIT回应Scott'的评论。
唯一影响GC性能指标的时候是调用[原文如此]GC.Collect()的时候"
从概念上讲,GC维护着一个对象引用图的视图,以及来自线程堆栈框架的所有引用。 这个堆可能相当大,跨越了许多内存页。 作为一种优化,GC对那些不太可能经常变化的页面进行缓存分析,以避免不必要地重新扫描该页面。 当页面中的数据发生变化时,GC会收到来自内核的通知,所以它知道这个页面是脏的,需要重新扫描。 如果集合是在第0代,那么很可能页面中的其他东西也在发生变化,但这在第1代和第2代中不太可能。 据传闻,为了让Silverlight插件在Mac平台上工作,将GC移植到Mac上的团队,在Mac OS X上没有这些钩子。
另一点是反对不必要的资源处置:设想一个进程正在卸载的情况。 想象一下,这个进程已经运行了一段时间了。有可能该进程的许多内存页已经被交换到了磁盘上。 至少它们已经不在L1或L2高速缓存中了。 在这种情况下,正在卸载的应用程序没有必要将所有这些数据和代码页换回内存,以释放那些在进程终止时将被操作系统释放的资源。 这适用于管理型资源,甚至某些非管理型资源。 只有那些让非后台线程存活的资源必须被处理掉,否则进程将继续存活。
现在,在正常的执行过程中,有一些短暂的资源必须被正确地清理掉(正如@fezmonkey指出的数据库连接、套接字、窗口句柄)以避免非托管的内存泄漏。 这些都是必须被处理掉的东西。 如果你创建了一些拥有线程的类(我说的拥有是指它创建了线程,因此有责任确保它停止,至少按照我的编码风格是这样),那么这个类很可能必须实现IDisposable',并在
Dispose'期间拆解线程。
.NET框架使用IDisposable
接口作为信号,甚至是警告,告诉开发者这个类必须被处理掉。 我想不出框架中实现`IDisposable'的任何类型(不包括明确的接口实现),处置是可选的。