C#处理IDisposable

有人可以解释如果你不Dispose一些IDisposable实体(通过using或直接Dispose调用)可能会发生什么?

这是否总是导致内存泄漏?如果是, C#内存泄漏类似于C++内存泄漏,它们很容易导致崩溃,或者从这个角度看C#沙箱是否更安全?

谢谢。

它完全取决于所讨论的对象。

对象可能实现IDisposable四个五个原因:

  1. 它拥有本地资源。
    如果是这样,它应该在其终结器中调用Dispose ,所以如果你忘记处理它就会发生这种情况,那就是本机资源的使用时间超过了必要的时间。
    如果您一次创建大量此类对象并且不进行处置,则可能会耗尽本机资源。

  2. 它拥有一次性的托管对象。
    它拥有的对象也属于这些类别之一; 查看相关类别。

  3. 它具有对自身的持久引用,不会自然释放(例如,它处理static事件)
    dispose方法将清除此引用(例如,取消注册事件处理程序)
    如果是这样,不调用Dispose将导致它永远持续下去。

  4. 它inheritance了一次性基类,例如StreamComponent ,但实际上并不需要处理。 (例如, MemoryStream
    如果是这样,不调用Dispose是无害的,除非该类将来发生变化。

  5. 它在Dispose方法中执行一些操作,人们应该在using语句中调用它(例如, using (Html.BeginForm() { ... }
    如果是这样,不处置该对象将失去其整个目的,即执行该操作。

值得指出的一个区别是.NET管理内存,而不是资源IDisposable的设计使得实现此接口的类的客户端可以确定地处理资源。

Joe Duffy在题为DG Update:Dispose,Finalization和Resource Management的post中非常清楚地说明了这一区别:

CLR的垃圾收集器(GC)在管理直接分配给CLR对象的内存方面做得非常出色,但明确没有设计用于处理非托管内存和操作系统管理的资源。

任何数量的事情都可能发生,因为一个类可以自由地实现Dispose但是他们选择了。

通常处理的不是托管资源(通常自行处理),而是非托管资源,如数据库连接,文件锁等。

一般来说,当垃圾收集到达时,在终结器中调用Disposed,但是例如,如果你写入文件而你没有关闭它(Dispose这样做)那么你就是在阻止任何其他程序(包括其他部分)打开该文件(除了可能的只读模式),你的代码在同一个程序中)。

如果您持续数据库连接的时间超过了必要的时间,则可以访问最大打开连接并停止使用数据库。

通常,如果一个类实现了IDisposable那就说我有一些需要处理的东西,它应该尽快处理,而不是等待垃圾收集器出现。

最可能的影响将是资源锁定(例如,您打开文件句柄并且不关闭它 – 最终终结器将处理它)或耗尽(例如,您执行绘图操作并且不处理对象)。

这可能是一个问题,特别是对于后一个问题,如果内存压力不足以导致GC(将启动最终化过程)及时使用更多资源。 这导致了有用的错误,因为“GDI +中发生了一般错误”。

如果只有Dispose方法而没有终结器,写得不好的对象可能会泄漏内存,但这很少见。 另一种可能性是Dispose方法取消了静态事件的订阅。 这也可能是一个重大问题,并将“泄漏”内存。

不调用.Dispose()可能会导致内存泄漏。 否则,不调用Dispose()只会导致GC延迟收集。 总的来说,效果将完全取决于实例化的对象( 请参阅SLaks以获取精彩的项目符号列表 )。 通过实施IDisposable ,您可以负责清理混乱,可以这么说。

应该做什么实现是告诉GC(垃圾收集器)它应该抑制收集,因为你要处理它。 这样,当你的IDisposable对象上调用Dispose()方法时,你应该注意清理 – 即在该类中的对象上调用Dispose(),清除集合等。

而且,FYI, using{...}子句只能用于实现IDisposable的对象,因为在结束时自动调用Dispose()方法:

 using (MyDisposableObject dispObj = new MyDisposableObject()) { // use dispObj for some work } // dispObj.Dispose() is automatically called here 

IDisposable意味着包装非托管资源(即不是.NET内存)。 因此,如果您不处理它们,您将泄漏诸如打开数据库连接,窗口句柄,文件句柄等之类的内容,具体取决于您未能处理的IDisposable类型。

因此,它不是真正的内存泄漏,但它会导致性能下降,并最终导致崩溃,因为您耗尽了非托管资源。

有些对象在它们存在的过程中会向外部实体做一些事情,它们会被清理干净。 有一个非常强烈的约定,即只要实际实现IDisposable,这些对象应该在调用IDisposable.Dispose()方法时执行任何所需的清理。 系统允许对象在注意到它们已被放弃时请求通知,但不能保证这些通知会及时发生以便有用。 此外,除非一个人小心,否则这些通知可能会提前开火并尝试清理仍在使用的东西。

除非您知道可以安全地放弃特定类别的IDisposable对象,否则不要。 无论何时实际可以随时清理。 “终结”(通知他们被遗弃的物品的过程)是危险的,可以创造许多海森堡。 如果存在任何实际替代方案,请避免依赖它。