如果不使用 using 语句,IDisposable 内存会泄漏吗?
如果是这样,如果代码不多,有人可以提供内存泄漏示例吗?
6 回答
一个正确编写的程序,它创建了一个实现 的类型的实例IDisposable
,并且没有明确知道在被放弃时能够充分清理自身,必须确保Dispose
在放弃它之前在该实例上调用它。任何程序如果不能调用Dispose
一个没有它就可以正常工作的类型,就会被破坏。
虽然如果自动完成可以处理所有事情会很好,但它是一个非常糟糕的清理机制。它不提供关于排序、线程上下文、及时性或完成确定性的保证(使用确定性清理时,可以合理地确定如果清理失败,程序不会正常完成;使用终结时,程序可能似乎正常完成,甚至没有尝试清理对象)。
Microsoft 可能曾经打算让每个IDisposable
类在被放弃后都能够充分清理,但这根本不切实际。在许多情况下,如果一个类在被放弃后尝试自行清理会增加大量复杂性,并且只会将一个存在明显问题且易于追踪的损坏程序转变为通常可以工作的损坏程序除非终结器线程相对于其他线程的计时导致事情以某种意外和不可重现的方式失败。
尽管实施了某些类型,但IDisposable
可以无条件安全地放弃,还有一些在某些情况下可以安全地放弃。在处理它们很困难的情况下放弃这些类型是很好的(例如,因为引用由多个对象持有,这些对象由各种线程操作,并且当任何特定对象持有最后一个幸存者时,没有好的方法可以知道它参考),前提是一个人记录了一个人相信这种行动是安全和适当的理由。IDisposable
然而,这种行为在一个人接受了未知血统的对象的情况下是不合适的。
不,它不会泄漏。最终垃圾收集将绕过处理对象。
IDisposable
允许调用者提前释放资源。
更新
正如@Servy 和@Brian Rasmussen 所说。实现的类也IDisposable
应该实现终结器。这是推荐的做法。见http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.100).aspx
首先,请注意IDisposable 通常与外部非托管资源1相关- 例如文件、连接 - 以及此类资源的泄漏;GC 仍将根据可达性正确处理此类使用的 CLR 对象和内存。
IDisposable 定义了一个契约,并表示在添加到类型或从类型中删除时的重大更改。任何未能履行本合同的行为都可能导致“未明确定义的行为”。该using
构造是一种工具,可以避免处理调用 Dispose 和带有异常的边缘情况的细节,但它不是合同的一部分,也不是必需的。然而,合同保持不变,任何违反合同的行为都将消除上述 IDisposable 的“正常工作”的所有责任。
- 一些实现 IDisposable 的类型永远不会泄漏资源;他们可能不会处理任何外部资源。
- 一些实现 IDisposable 的类型不遵循也实现终结器的“最佳实践”;如果不调用 Dispose,它们将泄漏外部资源。
- 某些类型,例如那些也实现终结器模式的类型,可能只会在某些 GC 情况下泄漏外部资源。也就是说,终结器可能不会很快被调用。这种情况在低负载情况下可能太慢而不会成为问题,但在高负载情况下会导致意外故障。
- 某些类型可能会导致行为不明确和状态不一致的情况。
不要违反合同。
1 IDisposable 类型还可以更改 Dispose 中不使用非托管资源的某些状态:如果违反合同,这仍然包含在“未明确定义的行为”中。我使用 IDisposable 类型的一种情况是管理运行时可调用包装器 (RCW) 对象,即使它们在技术上是由运行时“管理”的。有关更多情况,请参阅 supercat 的评论。
这一切都取决于你IDisposable
是什么。
该IDisposable
模式基本上是一种确定性释放托管(和非托管)资源的方法,而不是等到该对象的终结器运行。例如,如果您打开数据库连接、文件句柄等,您当然希望“按需”释放或清理这些资源,因此它们不会阻止您在其他地方重新获取对它们的访问权限。这是该Dispose
模式的主要用例。
那么,它会泄漏内存吗?同样,这取决于 - 如果您使用IObervable<T>
订阅,很可能,因为从某种意义上说,它们是未发布的事件处理程序(我在这里大大简化了)。如果不关闭它会导致内存泄漏SqlConnection
吗?不是按照“内存泄漏”的最严格定义,因为连接最终会关闭(例如,当您的应用程序终止时,或者连接对象最终被收集和完成时),但我想我可以这样总结所有这些:
“总是丢弃你的 IDisposables”
编辑:@Servy 是绝对正确的——在我的SqlConnection
例子中,我相信终结器会自动关闭连接,但这不是IDisposables
一般的保证行为——所以总是处置!
using
声明只是语法糖
using(var resource = expression) statement
翻译喜欢的东西
{
var resource = expression;
try {
statement;
}
finally {
[if(resource!=null)]((IDisposable)resource).Dispose();
}
}
如果 dispose 模式正确实施,则不会发生内存泄漏。Finalizer
调用方法的 GC 调用(非确定性)Dispose
。
Using
语句将Dispose
在块的末尾调用对象方法。
您可以使用以下示例获得相同的结果:
obj a = new obj(); // Assuming obj : IDisposable
try
{
// Your code here
}
finally
{
if (a != null)
{
a.Dispose();
}
}