C#处理IDisposable
有人可以解释如果你不Dispose
一些IDisposable
实体(通过using
或直接Dispose
调用)可能会发生什么?
这是否总是导致内存泄漏?如果是, C#
内存泄漏类似于C++
内存泄漏,它们很容易导致崩溃,或者从这个角度看C#
沙箱是否更安全?
谢谢。
它完全取决于所讨论的对象。
对象可能实现IDisposable
有四个五个原因:
-
它拥有本地资源。
如果是这样,它应该在其终结器中调用Dispose
,所以如果你忘记处理它就会发生这种情况,那就是本机资源的使用时间超过了必要的时间。
如果您一次创建大量此类对象并且不进行处置,则可能会耗尽本机资源。 -
它拥有一次性的托管对象。
它拥有的对象也属于这些类别之一; 查看相关类别。 -
它具有对自身的持久引用,不会自然释放(例如,它处理
static
事件)
dispose方法将清除此引用(例如,取消注册事件处理程序)
如果是这样,不调用Dispose
将导致它永远持续下去。 -
它inheritance了一次性基类,例如
Stream
或Component
,但实际上并不需要处理。 (例如,MemoryStream
)
如果是这样,不调用Dispose
是无害的,除非该类将来发生变化。 -
它在
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对象,否则不要。 无论何时实际可以随时清理。 “终结”(通知他们被遗弃的物品的过程)是危险的,可以创造许多海森堡。 如果存在任何实际替代方案,请避免依赖它。