当然,我们应该在不需要IDisposable对象时调用Dispose()(这通常只是“using”语句的范围)。如果我们不采取这种预防措施,那么可能会发生从细微到显示停止的坏事。
但是在流程终止之前的“最后时刻”呢?如果您的IDisposables在那个时间点没有明确处理,那么它不再重要吗?我问,因为CLR下面的非托管资源由内核对象表示 - 而win32进程终止将释放所有非托管资源/内核对象。换句话说,在进程终止后,没有资源会保持“泄露”(无论是否在延迟的IDisposables上调用Dispose())。
任何人都可以想到一个流程终止仍会留下泄漏资源的情况,只是因为未在一个或多个IDisposable上显式调用Dispose()?
请不要误解这个问题:我并不是在试图忽视IDisposables。问题只是技术理论上的。
编辑:那么在Linux上运行单声道怎么样?在清理未管理的“泄漏”时,流程终止是否“可靠”?LATE EDIT:虽然IDisposable可能存在“其他用途”,但我的重点是资源泄漏。我听到了两个答案:(1)如果你的进程拒绝终止,你就会泄漏;(2)是的,即使进程终止,资源也可能泄漏。我当然同意第(1)项,尽管它超出了我所追求的范围。否则,第(2)项正是我正在寻找的,但我无法摆脱这只是猜测的感觉。 Jeffrey Richter(“通过C / C ++的Windows”)解释说,(优雅地)终止的Win32进程不会留下泄漏或孤立的资源。为什么包含CLR的进程会改变这种情况?在使用CLR时,文档,特定示例或理论场景在哪里可以说明Win32进程清理功能受到损害?
答案 0 :(得分:2)
从技术上讲,这一切都取决于IDisposable的作用。它已被用于很多事情,而不仅仅是非托管资源。
例如,在处理Outlook应用程序时,我构建了一个很好的Outlook API抽象。附件对于使用流特别恼人,因为你需要将它保存到临时文件,使用它,然后清理它。
所以我的抽象看起来像这样:
OutlookMailItem mailItem = blah;
using (Stream attachmentStream = mailItem.OpenAttachment("something.txt")) {
// work with stream
}
在AttachmentStream上调用Dispose时,它所基于的临时文件被删除。在这种情况下,如果未调用Dispose,则永远不会清除临时文件。我在启动时有一个进程来查找这些孤立的文件,但我想我会以此为例。
实际上,几乎所有包装某种套接字,句柄或事务的IDisposable实现都将在进程终止时由操作系统简单地清理。但显然这就像福利。如果可以,请避免使用它。
答案 1 :(得分:2)
在协作关闭期间,AppDomain被卸载,导致所有终结器执行:
来自Object.Finalize文档:
在关闭应用程序域期间,会自动调用Finalize,这些对象不会被终结,即使是那些仍然可以访问的对象。
只要满足两个条件,您就可以安全停机:
每个仍然存活的IDisposable
对象都有一个正确实现的终结器(对于Framework类来说,对于不太可靠的库可能不是这样);以及
实际上是合作关闭,而不是异常关机,例如硬件进程终止,控制台应用程序中的Ctrl-C或Environment.FailFast
。
如果未满足这两个标准中的任何一个,则您的应用程序可能会保留全局非托管资源(例如互斥锁),这些资源实际上会泄漏。因此,如果可以,最好尽早致电Dispose
。 大多数当时,你可以依靠CLR和对象终结器为你做这项工作,但最好是安全而不是抱歉。
答案 2 :(得分:1)
我经常发现自己处理的一件事是串口 - 现在,当程序放松时应该释放一个串口,其他程序在被另一个进程占用时无法访问串口。因此,如果您的进程拒绝死亡,那么您将占用一个串行端口。如果用户尝试重新启动程序,那可能会非常糟糕,但前一个进程的僵尸版仍然保留在串行端口上。
是的,我之前让我的程序发现自己处于僵尸状态,然后客户抱怨该程序不再工作,因为程序在重启时无法连接到串口。结果是通过在任务管理器中杀死进程或让他们重新启动来引导用户,这两者都不是特别用户友好的任务。
答案 3 :(得分:0)
您可以编写自定义代码以释放一次性类中的对象。编写代码以释放非托管和托管代码。
这就是我们需要Dispose
功能的原因。所有释放内存都不是自动的,就像你在非托管代码中所说的那样。
现在,如果您认为操作系统会自动释放非托管代码,则不然。如果应用程序未正确处理,有许多句柄和锁可能保持活动状态。
答案 4 :(得分:0)
首先,我想指出IDisposable
不仅适用于Windows内核对象或非托管内存。相反,它是垃圾收集不理解的东西(哪些内核对象是特殊情况)。
一个小例子,只是为了给你一个想法:
sealed class Logger : IDisposable
{
private bool _disposed = false;
public Logger()
{
System.IO.File.AppendAllText( @"C:\mylog.txt", "LOG STARTED" );
}
~Logger()
{
Dispose();
}
public void Dispose()
{
if ( !_disposed )
{
System.IO.File.AppendAllText( @"C:\mylog.txt", "LOG STOPPED" );
_disposed = true;
}
}
public void WriteMessage( string msg )
{
System.IO.File.AppendAllText( @"C:\mylog.txt", "MESSAGE: " + msg );
}
}
请注意,此Logger
类不“保留”任何内核对象样式的资源。它打开文件并立即关闭它。但是,有这样的逻辑,它应该在对象被销毁时写入“LOG STOPPED”。这个逻辑是GC无法理解的 - 这是使用IDisposable
的地方。
因此,您不能依靠Windows清理内核对象。甚至可能还有一些Windows不知道的东西。
然而,如果说你的一次性物品是正确书写的(即在终结器中调用Dispose),它们在处理退出时仍将由CLR处理。
所以,答案是:是的,没有必要在项目退出之前调用Dispose
。但不是因为一次性资源是内核对象(因为它们不一定是)。
修改强>
正如Josh Einstein正确指出的那样,终结者实际上并不能保证在流程退出时运行。那么答案就变成了:总是调用Dispose,只是为了起诉