C#资源释放方法与原理总结

.Net类型分为两大类:值类型引用类型
值类型分配在上,并不需要GC回收;
引用类型分配在上,它的内存释放和回收需要通过GC(Garbage Collecter) 垃圾回收器来完成。
一个引用类型对象所占用的内存需要被GC回收,需要先成为垃圾。下图是《C#图解教程中》对GC回收过程的图解。
C#资源释放方法与原理总结_第1张图片
C#中资源分为【托管资源】 和 【非托管资源】两种
非托管资源: 所有的Window内核对象(句柄)都是非托管资源,如Stream,数据库连接,GDI相关对象,还有Com对象等等。。。这些资源并不是受到CLR管理。

托管资源:由CLR管理分配和释放的资源,即由CLR里new出来的对象。

托管资源:并不需要显示释放,但是如果引用类型本身含有非托管资源,则需要进行现实释放。
非托管资源:需要显式释放的,也即需要你写代码释放。

所以一般在.Net中托管资源是不需要我们去手动释放的,我们只需要对非托管资源进行释放即可。

.Net提供了三种释放资源的方法:
1、继承IDisposable接口,使用Dispose()方法:

public void ExcuteCommand(string connectString, string commandString)
        {
            SqlConnection myConnection = new SqlConnection(connectString);
            SqlCommand myCommand = new SqlCommand(commandString, myConnection);
            myConnection.Open();
            myCommand.ExecuteNonQuery();
        }

由以上代码可知,有两个对象没有被释放掉:SqlConnection和SqlCommand。它们都会保存在内存中直到终结器被调用为止。

通过下面的修改,我们可以释放它们:

     public void ExcuteCommand(string connectString, string commandString)
     {
         SqlConnection myConnection = new SqlConnection(connectString);
         SqlCommand myCommand = new SqlCommand(commandString, myConnection);
         myConnection.Open();
         myCommand.ExecuteNonQuery();

         myCommand.Dispose();
         myConnection.Dispose();
     }

确保非托管资源会释放的最好方法是使用using或者try/finally。
在C#中使用using是释放非托管的最好方法,因为在using执行完成的时候,会自动调用Dispose()方法释放资源

using(SqlConnection myConnection = new SqlConnection(connectString))
{
   myConnection.Open();
}

也可以使用try/catch/finally语句块, 确保在finally块中关闭任何已打开的连接。

try
{
     SqlConnection myConnection = new SqlConnection(connectString);
     myConnection.Open();
}
catch
{
   //
}
finally
{undefined
     myConnection.Dispose();
}

2、析构方法(终结器):

析构函数 :
析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。
析构函数声明:

~Class()
{
	//语句
}

我们不能控制析构函数何时将被执行因为这是由GC决定的。析构函数不确定什么时候会被执行。所以如果你的代码中包含需要及时清理的非托管资源,别把它留给析构函数处理。

3、提供显示释放方法,比如常用的Close方法:

	....
	myConnection.Close();
	.....

虽然这个方法也能释放连接,但是它和Dispose()方法的原理不一样。

Dispose()方法除了释放资源之外,还有其他的工作:它会通知垃圾收集器(Garbage Collector)这个对象的资源已经被释放了,而不必在终结器中进行重复的操作。

Dispose()调用了**GC.SuppressFinalize()**方法,这个方法请求系统不要调用指定对象的终结器。

而使用Close()释放的对象不会调用终结器,但它还是存在于终结器的释放资源队列当中。Dispose()比Close()的工作做的更加彻底。

在C#中大部分的类型都不支持Dispose()。在超过1500种类型中只有100来种实现了IDispose接口。当我们使用实现了这个接口的对象时,我们应当在适当的时候使用using或try/finally块的方法来释放资源。
【译自《Effective C#:50 Specific Ways to Improve Your C# 》 Bill Wagner著】

总结:
Dispose()和Close()都是可以显示调用来释放资源,析构函数不确定什么时候会被执行。
被Close()掉的对象是有可能再次复活的,比如SqlConnection,还可以通过Open()继续使用,类似于【休眠】状态。
而被Dispose()掉的对象从理论上来说是无法再使用了,因为我们在Dispose的同时已经告诉GC这个对象已经over了,以避免重复的对象清除工作。

我们在编程时, 应该至少用【using语句】或者【try/catch/finally语句块】两种方式中的一种或者两种都使用来确保资源的释放。无论在哪里获得资源,最好都使用using语句,因为尽管我们都会用Close()或者Dispose()语句, 但有时会忘记,此时using语句就会发挥作用。这两种方式都没有好的异常处理方式来替代,所以在大多数情况下,最好组合使用这两种方法。

参考1:https://blog.csdn.net/marklr/article/details/4349777
参考2:http://blog.sina.com.cn/s/blog_49ac5263010005xs.html

你可能感兴趣的:(C#项目,c#,.net)