为什么不';t CLR处理清除代码
本文关键字:处理 清除 代码 CLR 为什么不 | 更新日期: 2024-07-27 07:30:24
我刚刚开始使用.NET框架。今天,我学习了IDisposable
接口和dispose()
方法。我学到了一些关于它的东西:
dispose()
应该包含对应于对象的清理代码(比如关闭任何对象占用的任何资源——文件或数据库连接等)
我还被告知,如果我们不在dispose()
方法中这样做,那么在析构函数中也可以这样做,但这不能确保立即执行,我们只能任由GC摆布。
如果我们根本不提供任何清理代码,GC将强制终止与我们的对象所持有的资源的所有连接。因此,我们应该自己处理清理代码。
但我很好奇CLR为什么不自己处理这个问题?它负责内存管理,负责垃圾回收。因此,它应该非常清楚哪个Object持有哪些资源,以及该Object何时消亡。因此,它也应该能够取消分配这些资源?
我问了几个人。我得到的答案是,这是因为我们需要优雅地关闭它,就像GC强行关闭它一样。这真的是原因吗?
在.NET中,GC所知道的远不止托管代码。涉及到大量的非托管代码:所有的文件句柄、数据库连接、网络套接字。。。所有这些都是普通的非托管Win32代码。你甚至不敢相信,在你从漂亮的C#应用程序调用的几乎每一个BCL函数中,你都会遇到大量用C++编写的非托管函数(上帝保佑VB6),这些函数深深地嵌入了操作系统本身的内部。所有这些函数都在分配非托管内存、句柄。。。被管理的世界不知道那里会发生什么。
例如,每次打开文件(FileStream
)时,基本上都在调用(当然是在幕后)CreateFile非托管Win32函数。此函数直接从文件系统分配非托管文件句柄。NET和GC完全没有办法跟踪这个非托管代码及其所做的一切。这就是为什么这些类实现IDisposable接口的原因。这样,您就可以始终使用语句包装它们的实例,并确保始终调用Dispose方法,即使在发生异常的情况下也是如此,并且要尽快调用。Dispose方法将负责调用另一个非托管函数来清理它所创建的混乱。
因此,基本上,您可以考虑IDisposable接口的方式如下:
当我们有了一个用完全托管语言编写的操作系统(例如微软研究公司的Midori)时,我们可能不再需要IDisposable,因为GC将能够完全取代它,因为它将了解系统中发生的一切。
IDisposable
和Dispose()
的要点是应该清理非托管内存。这是.NET没有分配的内存,这些内存来自外部来源,因此GC无法知道它。因此它无法自动为您清理。本质上,这正是托管内存和非托管内存之间的区别;-)
通常,您应该实现Dispose()
来清理类使用的任何非托管资源,并实现终结器来调用Dispose()
。不过,终结器只是一种保障。如果调用者忘记正确处理您的类,它将确保这些资源最终被清理。
IDisposable
接口为您提供了一种清理未管理资源的方法。CLR仅为您管理托管的资源。
换句话说,CLR只知道如何清理它管理的东西。如果您打开与系统其他部分的连接(如打开文件、数据库连接等),这些都是您的责任,您需要告诉CLR您希望它如何为您清理这些连接。
它只能负责.NET对象的内存管理。任何需要使用非托管资源的代码(例如,因为它与C++库交互)都不在垃圾收集器的权限范围内。所有这些代码都需要被告知何时以老式的方式释放资源。
.Net框架(和GC)无法知道如何释放非托管资源。它所能做的就是销毁托管代码对资源的引用。在与数据库服务器的连接上实际调用.Close()要好得多(从而告诉它连接应该返回到可用连接的轮询中),而不仅仅是破坏引用,并让它在设定的秒数后自行超时。
因此,在引用非托管资源时,请尽可能使用IDisposable接口!
IDisposable
在您不希望GC处理特定工件时使用。最常见的例子是连接或文件句柄。您不希望在释放文件或关闭与数据库的连接之前等待GC运行,因为您不知道什么时候会发生这种情况。
大多数人将IDisposable
与非托管资源相关联,这在很大程度上是准确的,但没有记住终结器是处理这些资源的正确.NET方式。IDisposable
提供了一种确定处理的方法,如果这对您的程序很重要的话。
IDisposable
接口只是一个约定,允许您果断地处置托管和非托管资源。它本身并不能取代垃圾收集,也不能做任何涉及垃圾收集器本身的事情。
这在非托管资源中更为明显,因为除非对这些资源进行处理(在终结器中或使用确定性处置),否则它们将一直作为内存泄漏,直到进程结束。对于托管内存,如果您不确定地处置这些项,GC将不确定地收集它们(假设最终有资格收集),因为它们是托管的(这也是处置模式在终结器路由中不包括托管项的原因)。
IDisposable
本身什么都不做,它只是一个公认的接口(并且在带有using
关键字的代码中得到支持),人们在处理使用可消耗资源、非托管内存、外部项目等的项目时希望找到它。
CLR不可能知道外部项何时完成。这完全取决于应用程序的流程。如果您碰巧也不知道何时处置对象,则终结器语法非常有用。如果在自定义类上实现终结器,则垃圾收集进程将在最终收集之前运行该终结器。这是你最后一次收拾自己的机会。
我们使用Dispose是为了将非托管资源作为文件访问或连接数据库进行处理,因为GC没有关于此非托管资源的信息。
您也可以使用Finalize,但它不具有性能,因为您将资源保存在Finalization结构中,GC在处置周期结束时通过此Finalization架构,并且它不具有性能