MSDN整理:
析构函数、Dispose()方法、Close()方法、using语句、GC.Collect...
1.非托管资源
.net托管资源:由CLR管理分配和释放的资源,一般是托管内存,.net中超过80%的资源都是托管资源。
.net非托管资源:由系统分配和释放的资源,(文件、窗口或网络连接等)
一般地在CLR里new 一个对象或者分配一个数组都不需要手动去释放内存,而如windows里的句柄资源常常需要手动释放,如字体、刷子、DC等。
2.垃圾回收
.NET Framework 的垃圾回收器管理应用程序的内存分配和释放。每次您使用 new 运算符创建对象时,运行库都从托管堆为该对象分配内存。只要托管堆中有地址空间可用,运行库就会继续为新对象分配空间。但是,内存不是无限大的。最终,垃圾回收器必须执行回收以释放一些内存。垃圾回收器优化引擎根据正在进行的分配情况确定执行回收的最佳时间。当垃圾回收器执行回收时,它检查托管堆中不再被应用程序使用的对象并执行必要的操作来回收它们占用的内存。
对于您的应用程序创建的大多数对象,可以依靠 .NET Framework 的垃圾回收器隐式地执行所有必要的内存管理任务。但是,在您创建封装非托管资源的对象时,当您在应用程序中使用完这些非托管资源之后,您必须显式地释放它们。最常见的一类非托管资源就是包装操作系统资源的对象,例如文件、窗口或网络连接。虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它不了解具体如何清理这些资源。对于这些类型的对象,.NET Framework 提供 Object.Finalize 方法,它允许对象在垃圾回收器回收该对象使用的内存时适当清理其非托管资源。默认情况下,Finalize 方法不执行任何操作。如果您要让垃圾回收器在回收对象的内存之前对对象执行清理操作,您必须在类中重写 Finalize 方法。
然而大家都可以发现在实际的编程中根本无法override方法Finalize(),在C#中,可以通过析构函数自动生成 Finalize 方法和对基类的 Finalize 方法的调用。
Finalize 方法不应引发异常,因为应用程序无法处理这些异常,而且这些异常会导致应用程序终止。
实现 Finalize 方法或析构函数对性能可能会有负面影响,因此应避免不必要地使用它们。
3.析构函数
-
不能在结构中定义析构函数。只能对类使用析构函数。
-
一个类只能有一个析构函数。
-
无法继承或重载析构函数。
-
无法调用析构函数。它们是被自动调用的。
-
析构函数既没有修饰符,也没有参数。
注:不应使用空析构函数。如果类包含析构函数,Finalize 队列中则会创建一个项。调用析构函数时,将调用垃圾回收器来处理该队列。如果析构函数为空,则只会导致不必要的性能丢失。
程序员无法控制何时调用析构函数,因为这是由垃圾回收器决定的。垃圾回收器检查是否存在应用程序不再使用的对象。如果垃圾回收器认为某个对象符合析构,则调用析构函数(如果有)并回收用来存储此对象的内存。程序退出时也会调用析构函数。
可以通过调用 Collect 强制进行垃圾回收,但大多数情况下应避免这样做,因为这样会导致性能问题
4.GC.Collect:强制垃圾回收
垃圾回收 GC 类提供 GC.Collect 方法,您可以使用该方法让应用程序在一定程度上直接控制垃圾回收器。通常情况下,您应该避免调用任何回收方法,让垃圾回收器独立运行。在大多数情况下,垃圾回收器在确定执行回收的最佳时机方面更有优势。
但是,在某些不常发生的情况下,强制回收可以提高应用程序的性能。当应用程序代码中某个确定的点上使用的内存量大量减少时,在这种情况下使用 GC.Collect 方法可能比较合适。
例如,应用程序可能使用引用大量非托管资源的文档。当您的应用程序关闭该文档时,您完全知道已经不再需要文档曾使用的资源了。出于性能的原因,一次全部释放这些资源很有意义。
在垃圾回收器执行回收之前,它会挂起当前正在执行的所有线程。如果不必要地多次调用 GC.Collect,这可能会造成性能问题。您还应该注意不要将调用 GC.Collect 的代码放置在程序中用户可以经常调用的点上。这可能会削弱垃圾回收器中优化引擎的作用,而垃圾回收器可以确定运行垃圾回收的最佳时间。
5.Dispose()方法和Close()方法
在正确实现 Dispose 方法时,Finalize 方法在未能调用 Dispose 方法的情况下充当防护措施来清理资源。请记住,执行 Finalize 方法会大大减损性能。如果您的 Dispose 方法已经完成了清理对象的工作,那么垃圾回收器就不必再调用对象的 Finalize 方法。
对于类型来说,若调用 Close 方法比调用 Dispose 方法更容易,则可以向基类型添加一个公共 Close 方法。Close 方法又会调用没有参数的 Dispose 方法,该方法可以执行正确的清理操作。下面的代码示例阐释了 Close 方法。
6.Using语句
请注意,using 语句只适用于这样的对象:这些对象的生存期不超过在其中构建这些对象的方法。
C# 通过 .NET Framework 公共语言运行库 (CLR) 自动释放用于存储不再需要的对象的内存。内存的释放具有不确定性;一旦 CLR 决定执行垃圾回收,就会释放内存。但是,通常最好尽快释放诸如文件句柄和网络连接这样的有限资源。
using 语句允许程序员指定使用资源的对象应当何时释放资源。为 using 语句提供的对象必须实现 IDisposable 接口。此接口提供了 Dispose 方法,该方法将释放此对象的资源。
可以在到达 using 语句的末尾时,或者在该语句结束之前引发了异常并且控制权离开语句块时,退出 using 语句。
可以在 using 语句中声明对象(如上所示),或者在 using 语句之前声明对象,如下所示:
可以有多个对象与 using 语句一起使用,但是必须在 using 语句内部声明这些对象,如下所示:
7.使用封装资源的对象
如果您要编写代码,而该代码使用一个封装资源的对象,您应该确保在使用完该对象时调用该对象的 Dispose 方法。要做到这一点,可以使用 C# 的 using 语句,或使用其他面向公共语言运行库的语言来实现 try/finally 块。
C# 编程语言的 using 语句通过简化必须编写以便创建和清理对象的代码,使得对 Dispose 方法的调用更加自动化。using 语句获得一个或多个资源,执行您指定的语句,然后处置对象。
当您用 C# 以外的语言编写托管代码时,如果该代码使用一个封装资源的对象,请使用 try/finally 块来确保调用该对象的 Dispose 方法。