来源:北大青鸟飞迅校区|发布时间:2013-04-17 10:06:08
如何清理C#.Net中的非托管代码?最近帮助其它项目组Review代码,发现有些地方实现了IDispose接口,同时也发现了一些关于IDispose的问题:
1、A类型实现了IDispose接口,B类型里面含有A类型的字段,B类型没有实现IDispose接口。
2、一个类里面实现了Finalize终结器,同时也实现了IDispose接口,但在Dispose方法里面没有调用GC.SuppressFinalize(this)方法。
下面我对以上两个问题分别分析一下,并提出解决方案。
问题1
如果A类型里面有非托管资源需要在实现的IDispose接口里面释放,由于B类型没有实现IDispose接口,B类型的使用者要想释放A类型的非托管资源并不方便.这样的话,就有可能忘记了释放A类型的非托管资源。
解决方案:
实现B类型的IDispose接口,在Dispose方法里面调用A类型的Dispose方法.这样,B类型的使用者在调用B类型Dispose的同时,就把A类型的Dispose也调用了。
问题2
在Dispose方法里面没有调用GC.SuppressFinalize(this)方法,会有什么问题呢,这样会导致垃圾回收器不能对 这个类型的对象及时回收. 当GC开始工作的时候,它首先将没有终结器的垃圾对象从内存中移除,有终结器的所有对象则添加到一个垃圾队列当中。GC会调用一个新线程来执行这些对象的 终结器。当终结器执行完毕后,这个对象会从队列中被移除。这个对象在队列中移除之后,当GC再次开始工作的时候,这个对象才能够被回收,所以有终结器的对 象会比没有的在内存中保留更长的时间。在后面我会对这里再详细的描述一下。
解决方案:
在Dispose方法中调用GC.SuppressFinalize(this)方法.这样的话,就不会把有终结器的对象则添加到垃圾队列当中.
切入正题
.net中,非托管代码清理有两种方式:Finalize方式和Dispose方式。
Finalize方式:通过对自定义类型实现一个Finalize方法来释放非通过资源。
从.net2.0开始,C#编译器不能对Finalize进行显示的调用和重写,必须使用析构函数来实现它。
class A
{
~A()
{
释放资源;
}
}
上面的代码就是通过Finalize方式来释放资源的跟C++用析构函数释放资源的代码很象。
但是它实现方式和C++不同,因为它是由垃圾回收器来管理内存的。
大家看到了,用Finalize方式释放非托管资源很简单,但是如果你了解了他的实现方式,你可能就不会选择用它来释放非托管资源。
那Finalize方式在.net内部是如何实现的呢?
当GC(垃圾回收器)开始工作的时候,它首先将没有终结器的垃圾对象从内存中移除,有终结器的所有对象则添加到一个终止化队列当中。GC会调用一个 新线程来执行这些对象的终结器。当终结器执行完毕后,这些对象会从队列中被移除。这时候由于这些对象在第一次检测到的时候没有被释放,它们将会进入第1代 对象,直到GC检测到第0代对象和第1代对象再次充满时,这时候GC才会把刚才那些对象释放掉,所以有终结器的对象会比没有的在内存中保留更长的时间。
招生热线: 4008-0731-86 / 0731-82186801
学校地址: 长沙市天心区团结路6号
Copyright © 2006 | 湖南大计信息科技有限公司 版权所有
湘ICP备14017520号-3