非托管资源的释放 和 实现IDisposible接口的作用
原文:http://www.cnblogs.com/xuezhi/archive/2012/12/06/2804423.html
托管和非托管都可以又gc回收
非托管资源的释放
与C++一样,C#可以定义析构函数。但C#的析构函数主要用于释放非托管资源。在Net中,由GC垃圾回收线程掌握对象资源的释放,程序员无法掌控析构函数的调用时机。为了完全掌控非托管资源的释放,Net提供一个IDisposable接口。
问题:Finalize()和Dispose()之间的区别?
最简单的解释是:Finalize自动释放资源,Dispose()用于手动释放资源。Finalize很像C++的析构函数,在代码中的实现也很像,但它的调用过程却大不相同。例如类A中实现了Finalize函数,在A的一个对象a被创建时,它的指针被插入到一个finalization链表中,在GC运行时,它将查找finalization链表中的对象指针,如果此时a已经是垃圾对象的话,它会被移入到一个freachable队列中,最后GC会调用一个高优先级线程,此线程专门负责遍历freachable队列并调用队列中所有对象的Finalize方法,至此,对象a中的非托管资源才得到释放,而a所占用的内存资源则必须等到下一次GC才能释放,所以一个实现了Finaliza方法的对象必须等两次GC才能被完全释放。
由于Finalize是由GC负责调用,可以说是一种自动的释放方式。里面牵涉到两个问题:1是无法确定GC何时运作,有可能很长时间不释放对象资源。2是会带来微妙的依赖性问题,因为负责的线程并不保证对象的Finalize调用的顺序。可见自动释放资源不能满足我们的需求,因为我们不能显式地调用它(它只能由GC调用),而且具有依赖性的问题,我们需要更准确地控制资源的释放。
Dispose方法:
Dispose是提供给我们显式调用的方法。
using (){}能自动调用Dispose方法。
在计算机科学中,内存泄漏(memory leak)指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。内存泄漏与许多其他问题有着相似的症状,并且通常情况下只能由那些可以获得程序源代码的程序员才可以分析出来。然而,有不少人习惯于把任何不需要的内存使用的增加描述为内存泄漏,严格意义上来说这是不准确的。
一般我们常说的内存泄漏是指堆内存的泄漏。堆内存是指程序从堆中分配的,大小任意的(内存块的大小可以在程序运行期决定),使用完后必须显式释放的内存。应用程序一般使用malloc,calloc,realloc等函数(C++中使用new操作符)从堆中分配到一块内存,使用完后,程序必须负责相应的调用free或delete释放该内存块,否则,这块内存就不能被再次使用,我们就说这块内存泄漏了。
http://bbs.csdn.net/topics/390156211
原文:http://www.cnblogs.com/jiebian/p/3409747.html
.net非托管资源
常见的非托管资源包括数据库链接、文件句柄、网络链接、互斥体、COM对象、套接字、位图和GDI+对象等
非托管资源的清理,主要有两种方式:Finalize 方法和 Dispose 方法,这两种方法提供了在垃圾收集执行前进行资源清理的方法。Finalize 方式,又称为终止化操作,其大致的原理为:通过对自定义类型实现一个Finalize 方法来释放非托管资源,而终止化操作在对象的内存回收之前通过调用 Finalize方法来释放资源;Dispose 模式,指的是在类中实现IDisposable 接口,该接口中的Dispose 方法定义了显式释放由对象引用的所有非托管资源。因此,Dispose 方法提供了更加精确的控制方式,在使用上更加的灵活
在继承链中所有实例将递归调用 base.Finalize 方法,也就是意味调用终结器释放资
源时,将释放所有的资源,包括父类对象引用的资源。因此,在 C#中,也无需调用或重写Object.Finalize 方法,事实上显示的重写会引发编译时错误,只需实现虚构函数即可。
{
l 重写了Finalize方法的类型对象,其引用类型对象的代龄将被提升,从而带来内存压力。
l Finalize方法在某些情况下可能不被执行,例如可能某个终结器被无限期的阻止,则其他终结器得不到调用。因此,应该确保重写的 Finalize方法尽快被执行。
基于以上原因,应该避免重写 Finalize 方法,而实现 Dispose 模式来完成对非托管资源的清理操作,具体实现见下文描述。
对于Finalize 方法,有以下规则值得总结:
l 在 C#中无法显示的重写Finalize方法,只能通过析构函数语法形式来实现。
l struct中不允许定义析构函数,只有 class中才可以,并且只能有一个。
l Finalize方法不能被继承或重载。
l 析构函数不能加任何修饰符,不能带参数,也不能被显示调用,唯一的例外是在子类重写时,通过base调用父类Finalize方法,而且这种方式也被隐式封装在析构函数中。
l 执行垃圾回收之前系统会自动执行终止化操作。
l Finalize方法中,可以实现使得被清理对象复活的机制,不过这种操作相当危险,而且没有什么实际意义,仅作参考,不推荐使用:对于重写了Finalize 方法的类型来说,可以通过GC. SuppressFinalize 来免除终结。对于Finalize 方式来说,存在如下几个弊端,因此一般情况下在自定义类型中应避免重写 Finalize 方法,这些弊端主要包括:
l 终止化操作的时间无法控制,执行顺序也不能保证。因此,在资源清理上不够灵活,也可能由于执行顺序的不确定而访问已经执行了清理的对象。
l Finalize方法会极大地损伤性能,GC使用一个终止化队列的内部结构来跟踪具有 Finalize方法的对象。当重写了Finalize方法的类型在创建时,要将其指针添加到该终止化队列中,由此对性能产生影响;另外,垃圾回收时调用Finalize方法将同时清理所有的资源,包括其父类对象的资源,也是影响性能的一个因素。
}
{
l 在该模式中,公有Dispose方法通过调用重载虚方法 Dispose(bool disposing)方法来实现,具体的资源清理操作实现于虚方法中。两种策略的区别是:disposing参数为真时,Dispose方法由用户代码调用,可释放托管或者非托管资源;disposing参数为假时,Dispose方法由Finalize调用,并且只能释放非托管资源。
l disposed字段,保证了两次调用Dispose方法不会抛出异常,值得推荐。
l 派生类中实现Dispose模式,应该重写基类的受保护Dispose方法,并且通过base调用基类的Dispose方法,以确保释放继承链上所有对象的引用资源,在整个继承层次中传播 Dispose模式。
protected override void Dispose(bool disposing)
{
if (!disposed)
{
try
{
//子类资源清理
//......
disposed = true;
}
finally
{
base.Dispose(disposing);
}
}
}
l 另外,基于编程习惯的考虑,一般在实现Dispose方法时,会附加实现一个Close方法来达到同样的资源清理目的,而Close内部其实也是通过调用Dispose来实现的。 }
//实现一个处理资源清理的具体方法
protected virtual void Dispose(bool disposing)
{
if (! disposed)
{
if (disposing)
{
//清理托管资源
}
//清理非托管资源
if (_handle != IntPtr.Zero)
{
//执行资源清理,在此为关闭对象句柄
CloseHandle(_handle);
_handle = IntPtr.Zero;
}
}
disposed = true;
}
public void Close()
{
//在内部调用Dispose来实现
Dispose();
}
}
在上述实现Dispose模式的典型操作中,有几点说明:
l Dispose方法中,应该使用 GC. SuppressFinalize防止 GC调用Finalize方法,因为显式调用Dispose显然是较佳选择。
l 公有Dispose方法不能实现为虚方法,以禁止在派生类中重写。.Dispose模式
另一种非托管资源的清理方式是Dispose 模式,其原理是定义的类型必须实现 System.IDisposable接口,该接口中定义了一个公有无参的 Dispose 方法,用户可以在该方法中实现对非托管资源的清理操作。在此,我们实现一个典型的Dispose模式:
class MyDispose : IDisposable
{
//定义一个访问外部资源的句柄
private IntPtr _handle;
//标记Dispose是否被调用
private bool disposed = false;
//实现IDisposable接口
public void Dispose()
{
Dispose(true);
//阻止GC调用Finalize方法
GC.SuppressFinalize(this);
}
原文:http://www.studyofnet.com/news/352.html
Finalize、Dispose保证了
(1)、 Finalize只释放非托管资源;
(2)、 Dispose释放托管和非托管资源;
(3)、 重复调用Finalize和Dispose是没有问题的;
(4)、 Finalize和Dispose共享相同的资源释放策略,因此他们之间也是没有冲突的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决