C#垃圾回收Finalize和Dispose的理解
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
C#垃圾回收Finalize和Dispose的理解
C# 中的析构函数实际上是重写了 System.Object 中的虚⽅法 Finalize
三种最常的⽅法如下:
1.析构函数;(由GC调⽤,不确定什么时候会调⽤)
2.继承IDisposable接⼝,实现Dispose⽅法;(可以⼿动调⽤。
⽐如数据库的连接,SqlConnection.Dispose(),因为如果及时释放会影响数据库性能。
这时候会⽤到这个,再如:⽂件的打开,如果不释放会影响其它操作,如删除操作。
调⽤Dispose后这个对象就不能再⽤了,就等着被GC回收。
)
3.提供Close⽅法。
(类似Dispose但是,当调⽤完Close⽅法后,可以通过Open重新打开)
析构函数不能显⽰调⽤,⽽对于后两种⽅法来说,都需要进⾏显⽰调⽤才能被执⾏。
⽽Close与Dispose这两种⽅法的区别在于,调⽤完了对象的Close⽅法后,此对象有可能被重新进⾏使⽤;⽽Dispose⽅法来说,此对象所占有的资源需要被标记为⽆⽤了,也就是此对象要被销毁,不能再被使⽤。
例如常见.Net类库中的SqlConnection这个类,当调⽤完Close⽅法后,可以通过Open重新打开⼀个数据库连接,当彻底不⽤这个对象了就可以调⽤Dispose⽅法来标记此对象⽆⽤,等待GC回收。
明⽩了这两种⽅法的意思后,⼤家在往⾃⼰的类中添加的接⼝时候,不要歪曲了这两者意思。
析构函数
Dispose⽅法
Close⽅法
意义
销毁对象
销毁对象
关闭对象资源
调⽤⽅式
不能被显⽰调⽤,在GC回收是被调⽤
需要显⽰调⽤
或者通过using语句
需要显⽰调⽤
调⽤时机
不确定
确定,在显⽰调⽤或者离开using程序块
确定,在显⽰调⽤时
下⾯提供⼀个模式来结合上⾯的析构函数和Dispose⽅法。
public class BaseResource: IDisposable
{
//前⾯我们说了析构函数实际上是重写了 System.Object 中的虚⽅法 Finalize, 默认情况下,⼀个类是没有析构函数的,也就是说,对象被垃圾回收时不会被调⽤Finalize⽅法
~BaseResource()
{
// 为了保持代码的可读性性和可维护性,千万不要在这⾥写释放⾮托管资源的代码
// 必须以Dispose(false)⽅式调⽤,以false告诉Dispose(bool disposing)函数是从垃圾回收器在调⽤Finalize时调⽤的
Dispose(false);
}
// ⽆法被客户直接调⽤
// 如果 disposing 是 true, 那么这个⽅法是被客户直接调⽤的,那么托管的,和⾮托管的资源都可以释放
// 如果 disposing 是 false, 那么函数是从垃圾回收器在调⽤Finalize时调⽤的,此时不应当引⽤其他托管对象所以,只能释放⾮托管资源
protected virtual void Dispose(bool disposing)
{
// 那么这个⽅法是被客户直接调⽤的,那么托管的,和⾮托管的资源都可以释放
if(disposing)
{
// 释放托管资源
OtherManagedObject.Dispose();
}
//释放⾮托管资源
DoUnManagedObjectDispose();
// 那么这个⽅法是被客户直接调⽤的,告诉垃圾回收器从Finalization队列中清除⾃⼰,从⽽阻⽌垃圾回收器调⽤Finalize⽅法.
if(disposing)
GC.SuppressFinalize(this);
}
//可以被客户直接调⽤
public void Dispose()
{
//必须以Dispose(true)⽅式调⽤,以true告诉Dispose(bool disposing)函数是被客户直接调⽤的
Dispose(true);
}
}
上⾯的范例达到的⽬的:
1/ 如果客户没有调⽤Dispose(),未能及时释放托管和⾮托管资源,那么在垃圾回收时,还有机会执⾏Finalize(),释放⾮托管资源,但是造成了⾮托管资源的未及时释放的空闲浪费
2/ 如果客户调⽤了Dispose(),就能及时释放了托管和⾮托管资源,那么该对象被垃圾回收时,不回执⾏Finalize(),提⾼了⾮托管资源的使⽤效率并提升了系统性能
最后:
如果您的类中使⽤了⾮托管资源,则要考虑提供Close⽅法,和Open⽅法。
并在您的Dispose⽅法中先调⽤ Close⽅法。
在使⽤已经有类时,如SqlConnection。
如果暂时不⽤这个连接,可以考虑⽤Close()⽅法。
如果不⽤了就考虑调⽤
Dispose()⽅法。