如何自动出口NET函数对非托管方案的方案

.net非托管资源的回收方法
投稿:shichen2014
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了.net非托管资源的回收方法,以实例形式详细分析归纳了.net非托管资源的两种回收方法,具有一定的参考借鉴价值,需要的朋友可以参考下
本文实例讲述了.net非托管资源的回收方法,分享给大家供大家参考。具体分析如下:
释放未托管的资源有两种方法
1、析构函数
2、实现System.IDisposable接口
一、析构函数&
构造函数可以指定必须在创建类的实例时进行的某些操作,在垃圾收集器删除对象时,也可以调用析构函数。析构函数初看起来似乎是放置释放未托管资源、执行一般清理操作的代码的最佳地方。但是,事情并不是如此简单。由于垃圾回收器的运行规则决定了,不能在析构函数中放置需要在某一时刻运行的代码,如果对象占用了宝贵而重要的资源,应尽可能快地释放这些资源,此时就不能等待垃圾收集器来释放了.&
代码如下:using S
using System.Collections.G
using System.L
using System.T
namespace MemRelease
&&& class Program
&&&&&&& ~Program()
&&&&&&&&&&& // Orders.
&&&&&&& static void Main(string[] args)
在IL DASM中,你会发现并没有这个析构的方法。C#编译器在编译析构函数时,会隐式地把析构函数的代码编译为Finalize()方法的对应代码,确保执行父类的Finalize()方法 看下这段代码中对于析构函数的编译:
代码如下:.method family hidebysig virtual instance void
&&&&&&& Finalize() cil managed
& // Code size&&&&&& 14 (0xe)
& .maxstack& 1
&&& IL_0000:& nop
&&& IL_0001:& nop
&&& IL_0002:& leave.s&&& IL_000c
& }& // end .try
&&& IL_0004:& ldarg.0
&&& IL_0005:& call&&&&&& instance void [mscorlib]System.Object::Finalize()
&&& IL_000a:& nop
&&& IL_000b:& endfinally
& }& // end handler
& IL_000c:& nop
& IL_000d:& ret
} // end of method Program::Finalize
使用析构函数来释放资源有几个问题:
1、与C++析构函数相比,C#析构函数的问题是他们的不确定性。在删除C++对象时,其析构函数会立即执行,但是由于垃圾收集器的工作方式,无法确定C#对象的析构函数何时执行。
2、C#析构函数的执行会延迟对象最终从内存中删除的时间。有析构函数的对象需要2次处理才能删除:第一次调用析构函数时,没有删除对象,第二次调用才真正删除对象。
二、IDisposable接口
IDisposable接口定义了一个模式,为释放未托管的资源提供了确定的机制,并避免产生析构函数固有的与垃圾函数器相关的问题。IDisposable接口声明了一个方法Dispose(),它不带参数,返回void。
1、MSDN建议按照下面的模式实现IDisposable接口
代码如下: public class Foo: IDisposable
&&&& public void Dispose()
&&&&&&& Dispose(true);
&&&&&&& GC.SuppressFinalize(this);
&&&& protected virtual void Dispose(bool disposing)
&&&&&&& if (!m_disposed)
&&&&&&&&&&& if (disposing)
&&&&&&&&&&& {
&&&&&&&&&&&&&& // Release managed resources
&&&&&&&&&&& }
&&&&&&&&&&& // Release unmanaged resources
&&&&&&&&&&& m_disposed =
&&&& ~Foo()
&&&&&&& Dispose(false);
&&&& private bool m_
在.NET的对象中实际上有两个用于释放资源的函数:Dispose和Finalize
(1)、Finalize的目的是用于释放非托管的资源,而Dispose是用于释放所有资源,包括托管的和非托管的
(2)、void Dispose(bool disposing)函数通过一个disposing参数来区别当前是否是被Dispose()调用
如果是被Dispose()调用,那么需要同时释放托管和非托管的资源。如果是被~Foo()(也就是C#的Finalize())调用了,那么只需要释放非托管的资源即可。
(3)、Dispose()函数是被其它代码显式调用并要求释放资源的,而Finalize是被GC调用的
在GC调用的时候Foo所引用的其它托管对象可能还不需要被销毁,并且即使要销毁,也会由GC来调用。因此在Finalize中只需要释放非托管资源即可。另外一方面,由于在Dispose()中已经释放了托管和非托管的资源,因此在对象被GC回收时再次调用Finalize是没有必要的,所以在Dispose()中调用GC.SuppressFinalize(this)避免重复调用Finalize。
然而,即使重复调用Finalize和Dispose也是不存在问题的,因为有变量m_disposed的存在,资源只会被释放一次,多余的调用会被忽略过去。
Finalize、Dispose保证了:
(1)、 Finalize只释放非托管资源;
(2)、 Dispose释放托管和非托管资源;
(3)、 重复调用Finalize和Dispose是没有问题的;
(4)、 Finalize和Dispose共享相同的资源释放策略,因此他们之间也是没有冲突的。
2、IDisposable例子
代码如下:namespace 资源回收
&&& class Program
&&&&&&& static void Main(string[] args)
&&&&&&&&&&& //使用using对实现IDisposable的类了进行资源管理
/*拿到一个对象的时候,首先判断这个对象是否实现了IDisposable接口,如果实现了,最好就用using包裹住这个对象,保证这个对象用完之后被释放掉,否则很可能出现资源泄露的问题
&&&&&&&&&&& using (Telphone t1 = new Telphone())
&&&&&&&&&&& {
&&&&&&&&&&&&&&& t1.Open();
&&&&&&&&&&&&&&& t1.Speak("hello");
&&&&&&&&&&&&&&& t1.Bomb();
&&&&&&&&&&&&&&& //t1.Dispose();//如果在这里调用了Dispose()方法释放资源,那么在执行t1.Open()方法就出错,电话线已经被剪断了,无法再打电话了
&&&&&&&&&&&&&&& t1.Open();
&&&&&&&&&&&&&&& t1.Speak("I am back!");
&&&&&&&&&&& }//代码执行到这里后,就会调用Dispose方法来进行资源回收
&&&&&&&&&&& Console.ReadKey();
&&& /// &summary&
&&& /// Telphone类实现了IDisposable接口
&&& /// &/summary&
&&& class Telphone : IDisposable
&&&&&&& /// &summary&
&&&&&&& /// 电话状态
&&&&&&& /// &/summary&
&&&&&&& private TelphoneS
&&&&&&& /// &summary&
&&&&&&& /// 打电话
&&&&&&& /// &/summary&
&&&&&&& public void Open()
&&&&&&&&&&& if (state == TelphoneState.Disposed)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& throw new Exception("电话线已经被剪断,无法打开!");
&&&&&&&&&&& }
&&&&&&&&&&& state = TelphoneState.O
&&&&&&&&&&& Console.WriteLine("拿起电话");
&&&&&&& /// &summary&
&&&&&&& /// 说话
&&&&&&& /// &/summary&
&&&&&&& /// &param name="s"&说话内容&/param&
&&&&&&& public void Speak(string s)
&&&&&&&&&&& if (state != TelphoneState.Open)
&&&&&&&&&&& {
&&&&&&&&&&&&&&& throw new Exception("没有连接");
&&&&&&&&&&& }
&&&&&&&&&&& Console.WriteLine(s);
&&&&&&& /// &summary&
&&&&&&& /// 挂掉电话
&&&&&&& /// &/summary&
&&&&&&& public void Bomb()
&&&&&&&&&&& state = TelphoneState.C
&&&&&&&&&&& Console.WriteLine("挂掉电话");
&&&&&&& IDisposable 成员
&&& /// &summary&
&&& /// 电话状态枚举
&&& /// &/summary&
&&& enum TelphoneState
&&&&&&& Open, Close, Disposed
程序运行结果如下图所示:
三、析构函数和IDisposable混合调用的例子
代码如下:public class ResourceHolder : IDisposable
&&&&& private bool isDispose =
&&&&& // 显示调用的Dispose方法
  public void Dispose()
&&&&&&&&&& Dispose(true);
&&&&&&&&& GC.SuppressFinalize(this);
&&&&&& // 实际的清除方法
  protected virtual void Dispose(bool disposing)
&&&&&&&&&&& if (!isDisposed)
&&&&&&&&&& {
&&&&&  &&&&&& if (disposing)
  &&&&&&&& {
&&&&&&&&&&&&&&&&&&&&& // 这里执行清除托管对象的操作.
&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&&&& // 这里执行清除非托管对象的操作
&&&&&&&&&&& }
 &&&&&&& isDisposed=
&&&&& // 析构函数
&&&&& ~ResourceHolder()
&&&&&&&&&&& Dispose (false);
希望本文所述对大家的asp.net程序设计有所帮助。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具.NET垃圾回收 – 非托管资源
其实在C#开发中,大部分资源都可以通过.NET垃圾回收机制进行回收,只用当我们使用非托管资源(原始的操作文件句柄,原始的非托管连接,非托管内存等等)的时候,我们才需要实现自己的资源清理代码。
.NET提供了两种释放非托管资源的方式,类型自己的Finalize方法和IDisposable接口的Dispose方法。
下面就来看看这两个跟垃圾回收相关的方法。
Finalize方法
在.NET的基类System.Object中,定义了名为Finalize()的虚方法,这个方法默认什么都不做。
我们可以为自定义的类型重写Finalize方法,在该方法中加入必要的非托管资源清理逻辑。当要从内存中删除这个类型的对象时,垃圾回收器会调用对象的Finalize方法。所以,无论.NET进行一次自发的垃圾回收,还是我们通过GC.Collect()进行强制垃圾回收,Finalize方法总是会被调用。另外,当承载应用程序的AppDomain从内存中移除时,同样会调用Finalize方法。
重写Finalize方法
假设我们现在有一个使用非托管资源的类型,那么我们就需要重写Finalize方法来进行非托管资源的清理,但是当通过下面的方式重写Finalize方法的时候,我们会得到一个编译错误。
class MyResourceWrapper
& & protected override void Finalize()
其实,当我们想要重写Finalize方法时,C#为我们提供了(类似C++)析构函数语法(C#终结器)来重写该方法。C#终结器和构造函数语法类似,方法名称都和类型名称一样;不同的是,终结器具有~前缀,并且不能使用访问修饰符,不接受参数,也不能重载,所以一个类只能有一个终结器。
class MyResourceWrapper
& & ~MyResourceWrapper()
& & & & Console.WriteLine(&release unmanaged resources&);
& & & & Console.Beep();
之所以C#只支持这种方式进行Finalize方法的重写,是因为C#编译器会为Finalize方法隐式地加入一些必需的基础代码。下面就是我们通过ILSpy查看到了IL代码,Finalize方法作用域内的代码被放在了一个try块中,然后不管在try块中是否遇到异常,finally块保证了Finalize方法总是能够被执行。
.method family hidebysig virtual&
& & instance void Finalize () cil managed&
& & // Method begins at RVA 0x2050
& & // Code size 31 (0x1f)
& & .maxstack 1
& & & & IL_0000: nop
& & & & IL_0001: ldstr &release unmanaged resources&
& & & & IL_0006: call void [mscorlib]System.Console::WriteLine(string)
& & & & IL_000b: nop
& & & & IL_000c: call void [mscorlib]System.Console::Beep()
& & & & IL_0011: nop
& & & & IL_0012: nop
& & & & IL_0013: leave.s IL_001d
& & } // end .try
& & finally
& & & & IL_0015: ldarg.0
& & & & IL_0016: call instance void [mscorlib]System.Object::Finalize()
& & & & IL_001b: nop
& & & & IL_001c: endfinally
& & } // end handler
& & IL_001d: nop
& & IL_001e: ret
} // end of method MyResourceWrapper::Finalize
当我们执行下面代码时,我们就可以听到系统蜂鸣声,像我们前面介绍的一样AppDomain被移除内存,类型终结器将被调用。
static void Main(string[] args)
& & MyResourceWrapper mr = new MyResourceWrapper();
Finalize的工作机制
Finalize的工作机制还是比较复杂的,这里只是简单的介绍,更多的原理大家可以自己网上查查。
当在托管堆上分配对象空间时,运行库会自动确定该对象是否提供一个自定义的Finalize方法。如果是这样,对象被标记为可终结的,同时一个指向这个对象的指针被保存在名为终结队列的内部队列中。终结队列是一个由垃圾回收器维护的表,它指向每一个在从堆上删除之前必须终结的对象。
当垃圾回收器确定到了从内存中释放一个对象的时间时,它检查终结队列上的每一个项,并将对象从堆上复制到另一个称作终结可达表(finalization reachable table的托管结构上。此时,下一个垃圾回收时将产生另外一个线程,为每一个在可达表中的对象调用Finalize方法。因此,为了真正终结一个对象,至少要进行两次垃圾回收。
从上面可以看到,Finalize方法的调用是相当消耗资源的。Finalize方法的作用是保证.NET对象能够在垃圾回收时清理非托管资源,如果创建了一个不使用非托管资源的类型,实现终结器是没有任何作用的。所以说,如果没有特殊的需求应该避免重写Finalize方法。
IDisposable接口
当垃圾回收生效时,可以利用终结器来释放非托管资源。然而,很多非托管资源都非常宝贵(如数据库和文件句柄),所以它们应该尽可能快的被清除,而不能依靠垃圾回收的发生。除了重写Finalize之外,类还可以实现IDisposable接口,然后在代码中主动调用Dispose方法来释放资源。
看一个例子:
class MyResourceWrapper:IDisposable
& & public void Dispose()
& & & & Console.WriteLine(&release resources with Dispose&);
& & & & Console.Beep();
class Program
& & static void Main(string[] args)
& & & & MyResourceWrapper mr = new MyResourceWrapper();
& & & & mr.Dispose();
同样,当我们显示的调用Dispose方法的时候,可以听到系统的蜂鸣声。
注意,通过Dispose进行资源的释放也是有潜在的风险的,因为Dispose方法需要被程序员显示的调用,如果代码中漏掉了Dispose的调用或者在Dispose调用之前产生了异常从而没有指定Dispose,那么有些资源可能就一直留在内存中了。
所以我们应该使用下面的方式保证Dispose方法可以被调用到:
static void Main(string[] args)
& & MyResourceWrapper mr = new MyResourceWrapper();
& & & & //do something wiht mr object
& & finally
& & & & mr.Dispose();
但是,每次编写Dispose的代码都使用try块会觉得很麻烦,还好C#中,我们可以重用using关键字来简化Dispose的调用。
重用using关键字
在C#中,using语句提供了一个高效的调用对象Dispose方法的方式。对于任何IDispose接口的类型,都可以使用using语句,而对于那些没有实现IDisposable接口的类型,使用using语句会导致一个编译错误。
static void Main(string[] args)
& & using (MyResourceWrapper mr = new MyResourceWrapper())
& & & & //do something with mr object
在using语句块结束的时候,mr实例的Dispose方法将会被自动调用。using语句不仅免除了程序员输入Dispose调用的代码,它还保证Dispose方法被调用,无论using语句块顺利执行结束,还是抛出一个异常。事实上,C#编译器为using语句自动添加了try/finally块。我们可以看看using的IL代码:
& & IL_0007: nop
& & IL_0008: nop
& & IL_0009: leave.s IL_001b
} // end .try
& & IL_000b: ldloc.0
& & IL_000c: ldnull
& & IL_000d: ceq
& & IL_000f: stloc.1
& & IL_0010: ldloc.1
& & IL_0011: brtrue.s IL_001a
& & IL_0013: ldloc.0
& & IL_0014: callvirt instance void [mscorlib]System.IDisposable::Dispose()
& & IL_0019: nop
& & IL_001a: endfinally
} // end handler
Dispose和Finalize的结合
从前面的介绍了解到,Finalize可以通过垃圾回收进行自动的调用,而Dispose需要被代码显示的调用,所以,为了保险起见,对于一些非托管资源,还是有必要实现终结器的。也就是说,如果我们忘记了显示的调用Dispose,那么垃圾回收也会调用Finalize,从而保证非托管资源的回收。
其实,MSDN上给我们提供了一种很好的模式来实现IDisposable接口来结合Dispose和Finalize,例如下面的代码:
class MyResourceWrapper:IDisposable
& & private bool IsDisposed= &
& & public void Dispose() &
& & & & Dispose(true); &
& & & & //tell GC not invoke Finalize method
& & & & GC.SuppressFinalize(this); &
& & protected void Dispose(bool Disposing) &
& & & & if(!IsDisposed) &
& & & & { &
& & & & & & if(Disposing) &
& & & & & & {
& & & & & & & & //clear managed resources
& & & & & & }
& & & & & & //clear unmanaged resources
& & & & } &
& & & & IsDisposed= &
& & ~MyResourceWrapper() &
& & & & Dispose(false); &
在这个模式中,void Dispose(bool Disposing)函数通过一个Disposing参数来区别当前是否是被Dispose()调用。如果是被Dispose()调用,那么需要同时释放托管和非托管的资源。如果是被终结器调用了,那么只需要释放非托管的资源即可。Dispose()函数是被其它代码显式调用并要求释放资源的,而Finalize是被GC调用的。
另外,由于在Dispose()中已经释放了托管和非托管的资源,因此在对象被GC回收时再次调用Finalize是没有必要的,所以在Dispose()中调用GC.SuppressFinalize(this)避免重复调用Finalize。同样,因为IsDisposed变量的存在,资源只会被释放一次,多余的调用会被忽略。
所以这个模式的优点可以总结为:
如果没有显示的调用Dispose(),未释放托管和非托管资源,那么在垃圾回收时,还会执行Finalize(),释放非托管资源,同时GC会释放托管资源
如果调用了Dispose(),就能及时释放了托管和非托管资源,那么该对象被垃圾回收时,就不会执行Finalize(),提高了非托管资源的使用效率并提升了系统性能
本文介绍了.NET垃圾回收中两个相关的方法:Dispose和Finalize。Finalize的目的是用于释放非托管的资源,而Dispose是用于释放所有资源,包括托管的和非托管的。
Dispose需要在代码中进行显示的调用,而Finalize则是由垃圾回收自动调用,为了更有效的结合Dispose和Finalize,文中还介绍了MSDN中给出的实现IDisposable接口的一个模式。关于GC——Dotnet中Dispose的设计模式C语言教程【软件开发吧】_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:122,327贴子:
关于GC——Dotnet中Dispose的设计模式C语言教程收藏
.NET的GC机制有这样两个问题:首先,GC并不是能释放所有的资源。它不能自动释放非托管资源。第二,GC并不是实时性的,这将会造成系统性能上的瓶颈和不确定性。为了解决第一个问题,.NET提供了析构函数,在C#中是~ClassName的形式。如果某个类定义了析构函数,.NET会在第一次的GC中调用析构函数,第二次才真正进行资源释放。这就允许了我们能够做一些手动的资源管理操作,手动对非托管资源进行清理。但是如果没有必要,定义析构函数就会对性能造成较大的影响。仅仅依赖析构函数对非托管资源进行释放是不够的,这是由于第二个问题:GC并不是实时性的,这会造成系统性能上的瓶颈和不确定性。所以有了IDisposable接口,IDisposable接口定义了Dispose方法,这个方法用来供程序员显式调用以释放非托管资源。通常我们应该这样写程序:
public class SampleClass : System.IDisposable
public void Dispose()
//供程序员显式调用的Dispose方法
Dispose(true);
//调用带参数的Dispose方法,释放托管和非托管资源
System.GC.SuppressFinalize(this);
//手动调用了Dispose释放资源,那么析构函数就是不必要的了,这里阻止GC调用析构函数
protected void Dispose(bool disposing)
//protected的Dispose方法,保证不会被外部调用。
//传入bool值disposing以确定是否释放托管资源
if (disposing)
//在这里加入清理"托管资源"的代码,应该是xxx.Dispose();
// 在这里加入清理"非托管资源"的代码
~SampleClass()
//供GC调用的析构函数
Dispose(false);
//释放非托管资源
这样一来,我们就像Delphi里调用Object.Free方法一样自然的调用Object.Dispose方法,而即使我们忘记了在合适的时候调用Dispose,GC也会在释放对象的时候帮我们清理非托管资源的。GC所充当的角色只是一种保障手段,它应该充当这种角色,我们不能过分依赖它。
实际上,在较大的模块退出时我们还应该及时地手动调用GC.Collect进行垃圾回收。
登录百度帐号推荐应用}

我要回帖

更多关于 新开的托管班招生方案 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信