c#

位置:IT落伍者 >> c# >> 浏览文章

C#清理非托管对象[1]


发布日期:2020年01月06日
 
C#清理非托管对象[1]

这两天帮助其它项目组Review代码发现有些地方实现了IDispose接口同时也发现了一些关于IDispose的问题:

A类型实现了IDispose接口B类型里面含有A类型的字段B类型没有实现IDispose接口

一个类里面实现了Finalize终结器同时也实现了IDispose接口但在Dispose方法里面没有调用GCSuppressFinalize(this)方法

下面我对以上两个问题分别分析一下并提出解决方案

问题:如果A类型里面有非托管资源需要在实现的IDispose接口里面释放由于B类型没有实现IDispose接口B类型的使用者要想释放A类型的非托管资源并不方便这样的话就有可能忘记了释放A类型的非托管资源

解决方案:

实现B类型的IDispose接口在Dispose方法里面调用A类型的Dispose方法这样B类型的使用者在调用B类型Dispose的同时就把A类型的Dispose也调用了

问题:在Dispose方法里面没有调用GCSuppressFinalize(this)方法会有什么问题呢这样会导致垃圾回收器不能对 这个类型的对象及时回收 当GC开始工作的时候它首先将没有终结器的垃圾对象从内存中移除有终结器的所有对象则添加到一个垃圾队列当中GC会调用一个新线程来执行这些对象的 终结器当终结器执行完毕后这个对象会从队列中被移除这个对象在队列中移除之后当GC再次开始工作的时候这个对象才能够被回收所以有终结器的对 象会比没有的在内存中保留更长的时间在后面我会对这里再详细的描述一下

解决方案:

在Dispose方法中调用GCSuppressFinalize(this)方法这样的话就不会把有终结器的对象则添加到垃圾队列当中

切入正题

net中非托管代码清理有两种方式:Finalize方式和Dispose方式

Finalize方式:通过对自定义类型实现一个Finalize方法来释放非通过资源

net开始C#编译器不能对Finalize进行显示的调用和重写必须使用析构函数来实现它

class A

{

~A()

{

释放资源;

}

}

上面的代码就是通过Finalize方式来释放资源的跟C++用析构函数释放资源的代码很象

但是它实现方式和C++不同因为它是由垃圾回收器来管理内存的

大家看到了用Finalize方式释放非托管资源很简单但是如果你了解了他的实现方式你可能就不会选择用它来释放非托管资源

那Finalize方式在net内部是如何实现的呢?

当GC(垃圾回收器)开始工作的时候它首先将没有终结器的垃圾对象从内存中移除有终结器的所有对象则添加到一个终止化队列当中GC会调用一个 新线程来执行这些对象的终结器当终结器执行完毕后这些对象会从队列中被移除这时候由于这些对象在第一次检测到的时候没有被释放它们将会进入第代 对象直到GC检测到第代对象和第代对象再次充满时这时候GC才会把刚才那些对象释放掉所以有终结器的对象会比没有的在内存中保留更长的时间

提示:垃圾回收器把托管堆中的对象分为分别是一般分配为:代约K代约是MB代约是MB代龄越高容量就越 大显然效率也就越低首先被添加到托管堆中的对象被定为第当第代充满时就会执行垃圾回收未被回收的对象代领将提升

由于以上原因应该避免仅使用Finalize方式释放非托管资源

Dispose模式:在自定义类中实现IDispose接口在接口中的Dispose方法中对非托管资源进行释放闲话少说上代码

public class MyResourceRelease: IDisposable

{

/// 保证资源只用释放一次

private bool _alreadyDisposed = false;

/// 用来判断释放资源的类别(托管和非托管)

protected virtual void Dispose(bool isDisposing)

{

if(_alreadyDisposed)

{

return;

}

if(isDisposing)

{

//释放托管资源

}

//释放非托管资源

_alreadyDisposed = true;

}

public void Dispose()

{

Dispose(true);

}

}

上面的代码就是用Dispose方式释放资源的方法因为上面自定义的Dispose(bool isDisposing)方法是virtual的所以还可以在派生类里面对它进行override

[] []

               

上一篇:C#清理非托管对象[2]

下一篇:C#动态特性的更多消息