对托管资源使用终结器

重要提示:有的人可能有这样的心态,永远不要对托管资源使用终结器,我在很大程度上赞成这个观点,所以可以完全跳过本节,对托管资源使用终结器,是非常高的编码方式,只有极少数情况下才应该使用,要是使用必须对Finalize方法中的调用的代码有一个全面和深刻的认识。另外,还必须保证调用的代码的行为在未来的版本中不会发生改变。具体的说,Finalize方法中调用的任何代码都不能使用其他任何可能已终结的对象。

虽然终结操作是专门来释放本地资源,但偶尔也用于托管资源,下面这个类造成计算机在垃圾回收器每执行一次回收就发出响铃声。

sealed class GCBeep

{

~GCBeep()

{

if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted)

new GCBeep();

}

}

为了使用这个类,只需要构建该类的一个实例。然后,每次执行垃圾回收,都会调用对象的Finalize方法,该方法会调用Beep并构造一个新的GCBeep对象。下次垃圾回收时,这个新的GCBeep对象的Finalize方法将得到调用。如此反复,下面程序演示该操作:

static void Main(string[] args)

{

new GCBeep();

for (Int32 x = 0; x < 100000000; x++)

{

Console.WriteLine(x);

Byte[] b = new Byte[100];

}

}

另外注意的是,即使类的实例构造跑出了异常,类型的Finalize方法也会被调用。因此,你的Finalize方法不应假定对象处于良好、一致的状态。下面的代码演示了这一点。

sealed class TempFile

{

private String   m_filename = null;

private FileStream   m_fs;

public TempFile(string filename)

{

//下面这行代码可能抛出异常

m_fs = new FileStream(filename, FileMode.Create);

m_filename = filename;

}

~TempFile()

{

//正确的做法是测试filename是否为空,不能保证filename已经在构造中初始化。

if (m_filename != null) File.Delete(m_filename);

}

}

另外一种做法,

sealed class TempFile

{

private String   m_filename = null;

private FileStream   m_fs;

public TempFile(string filename)

{

try

{

//下面这行代码可能抛出异常

m_fs = new FileStream(filename, FileMode.Create);

m_filename = filename;

}

catch

{

GC.SuppressFinalize(this);//告诉GC不要调用Finalize方法

throw;//让调用者知道错误出现

}

}

~TempFile()

{

//这里不用判断,因为只有在构造成功之后才执行这里

File.Delete(m_filename);

}

}

设计一个类型时,出于以下几个性能方面的原因,最好是避免使用Finalize方法。

  1. 可终结的对象需要花更长的时间来分配,因为指向他们的指针必须放到终结者列表中。
  2. 可终结的对象会提升到比较老的一代,这会增大内存压力 ,并在垃圾回收器判定对象为垃圾时,阻止回收对象的内存。除此之外,该对象直接或间接引用的所有对象也会被提升。
  3. 可终结的对象导致应用程序速度变慢,这是因为每个对象在回收时,必须对他进行额外的处理。

除此之外,注意无法控制Finalize方法在什么时候运行,Finalize方法在垃圾回收器发生时运行,而垃圾回收期可能在应用程序请求更多的内存时发生。另外CLR不保证每个Finalize方法的调用顺序。因此,在写一个Finalize方法时,应避免访问定义了Finalize方法的其它类型的对象,那些对象可能已经终结,然而,可以完全放心的访问值类型的实例,或者访问没有定义Finalize方法的引用类型的实例。调用静态方法也得小心。

因为这些方法可能在内部访问以终结的对象,导致静态方法的行为变得无法预测。

时间: 2024-08-28 12:14:34

对托管资源使用终结器的相关文章

编写高质量代码改善C#程序的157个建议——建议50:在Dispose模式中应区别对待托管资源和非托管资源

建议50:在Dispose模式中应区别对待托管资源和非托管资源 真正资源释放代码的那个虚方法是带一个bool参数的,带这个参数,是因为我们在资源释放时要区别对待托管资源和非托管资源. 提供给调用者调用的显式释放资源的无参Dispose方法中,调用参数是true: public void Dispose() { //必须为true Dispose(true); //省略其他代码 } 这表明,这时候代码要同时处理托管资源和非托管资源. 在供垃圾回收器调用的隐式清理资源的终结器中,调用的是false:

使用终结器来释放本地资源

前面我们基本了解了垃圾回收和托管堆得情况了,包含垃圾回收期如何回收对象的内存,幸运的是,大多数类型只要内存就可以正常工作,但是,另外有一些类型除了使用内存,还要使用本地资源. 例如:System.IO.FileStream类型需要打开一个文件(本地资源)并保存文件的句柄.然后,该类型的Read和Write方法使用该句柄来操作文件,类似的,System.Threading.Mutex类型打开一个WINDOWS互斥体内核对象(本地资源)并保存其句柄,并在调用Mutex方法时使用该句柄.终结(fina

垃圾回收GC:.Net自动内存管理 上(三)终结器

垃圾回收GC:.Net自动内存管理 上(三)终结器 垃圾回收GC:.Net自动内存管理 上(一)内存分配 垃圾回收GC:.Net自动内存管理 上(二)内存算法 垃圾回收GC:.Net自动内存管理 上(三)终结器 前言 .Net下的GC完全解决了开发者跟踪内存使用以及控制释放内存的窘态.然而,你或午想要理解GC是怎么工作的.此系列文章中将会解释内存资源是怎么被合理分配及管理的,并包含非常详细的内在算法描述.同时,还将讨论GC的内存清理流程及什么时清理,怎么样强制清理. 终结器 GC提供了另外一个能

C#编程(七十四)----------释放非托管资源

释放非托管资源 在介绍释放非托管资源的时候,我觉得有必要先来认识一下啥叫非托管资源,既然有非托管资源,肯定有托管资源. 托管资源指的是.net可以自棕进行回收的资源,主要是指托管堆上分配的内存资源.托管资源的回收工作是不需要人工干预的,有.net运行库在合适的调用垃圾回收器进行回收. 非托管资源指的是.net不知道如何回收的资源,最常见的一类非托管资源是包装操作系统资源的对象,例如文件,窗口,网络连接,数据库连接,画刷,图标等.这类资源,垃圾回收器在清理的时候会调用Object.Finalize

托管资源和非托管资源

在.net 编程环境中,系统的资源分为托管资源和非托管资源. 对于托管的资源的回收工作,是不需要人工干预回收的,而且你也无法干预他们的回收,所能够做的只是了解.net CLR如何做这些操作.也就是说对于您的应用程序创建的大多数对象,可以依靠 .NET Framework 的垃圾回收器隐式地执行所有必要的内存管理任务.        资源分为两种,托管的内存资源,这是不需要我们操心的,系统已经为我们进行管理了:那么对于非托管的资源,这里再重申一下,就是Stream,数据库的连接,GDI+的相关对象

.net 资源释放(托管资源和非托管资源)

1.托管资源 像int.float.DateTime等都是托管资源:net中80%的资源都是托管资源: 托管资源的回收通过GC(垃圾回收器)自动释放分配给该对象的内存,但无法预测进行垃圾回收的时间,我们无法控制系统在什么时间回收资源. 2.非托管资源 像ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,FileStream,Font,Icon,Image,Matrix,Object,Odbc

垃圾回收GC:.Net自己主动内存管理 上(三)终结器

垃圾回收GC:.Net自己主动内存管理 上(三)终结器 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己主动内存管理 上(三)终结器 前言 .Net下的GC全然攻克了开发人员跟踪内存使用以及控制释放内存的窘态.然而,你或午想要理解GC是怎么工作的.此系列文章中将会解释内存资源是怎么被合理分配及管理的,并包括很具体的内在算法描写叙述.同一时候,还将讨论GC的内存清理流程及什么时清理,怎么样强制清理. 终结

C#析构函数(destructor)和终结器(Finalizer) .

使用析构函数释放资源 析构函数用于析构类的实例. 1)         不能在结构中定义析构函数.只能对类使用析构函数. 2)         一个类只能有一个析构函数. 3)         无法继承或重载析构函数. 4)         无法调用析构函数.它们是被自动调用的. 5)         析构函数既没有修饰符,也没有参数. 例如,下面是类 Car 的析构函数的声明: [csharp] view plaincopy class Car { /// <summary> /// 析构函

.NET对象的创建、垃圾回收、非托管资源的手动处理

本篇用来梳理对象的创建.垃圾的回收,以及非托管资源的手动处理. →首先运行应用程序,创建一个Windows进程. →CLR创建一块连续的虚拟地址空间,这个地址空间就是托管堆.而且,这个地址空间最初并没有对应的物理存储空间. 虚拟地址空间分成2段.一个区段是普通堆,也叫GC堆,大小小于85000字节的引用类型对象的实例被分配在这里:另一个是大对象堆,大小大于等于85000字节的引用类型对象的实例被分配在这里. 对于客户端应用程序,每个区段的大小大致是16MB:对于服务端应用程序,每个区段的大小大致