1、托管资源与非托管资源以及它们的释放
在.net 编程中,系统的资源分为托管资源和非托管资源。
对于托管的资源的回收工作,是不需要人工干预回收的,而且你也无法干预他们的回收。所能够做的只是了解.net CLR如何做这些操作。也就是说对于您的应用程序创建的大多数对象,可以依靠 .NET 的垃圾回收器(GC)隐式地执行所有必要的内存管理任务。
对于非托管资源,您在应用程序中使用完这些非托管资源之后,必须显示的释放他们,例如System.IO.StreamReader的一个文件对象,必须显示的调用对象的Close()方法关闭它,否则会占用系统的内存和资源,而且可能会出现意想不到的错误。
在.net framework中开发程序,因为framework是跨语言的平台,vb,c++等,所以需要托管代码,来对这些语言的代码进行托管编译,能被托管的代码叫托管资源,否则就非托管资源
最常见的一类非托管资源就是包装操作系统资源的对象,例如文件,窗口或网络连接,对于这类资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它不了解具体如何清理这些资源。还好.net Framework提供了Finalize()方法,它允许在垃圾回收器回收该类资源时,适当的清理非托管资源。
关于托管资源,像简单的int,string,float,DateTime等等,.net中超过80%的资源都是托管资源。
2、GC的资源回收-析构函数与Finalize()
GC自动回收的只有托管资源,但是GC回收并不是实时性的,GC是一个后台线程,周期性地查找需要回收的对象,内部通过一定的算法判断何时才是最适合回收的时机,自行回收“垃圾资源”,垃圾资源的判断条件是:不被任何引用指向的对象,但是我们可以通过析构函数判断该对象何时被GC回收,如:
class MainClass
{
public static void Main(string[] args)
{
for (int i = 0; i < 50000;i++)
{
A a = new A();
}
}
}
public class A
{
~A()
{
Console.WriteLine("GC had collected this object");
}
}
经过一定时间,我们就能在控制台看到一堆输出信息。当我们谈论GC的自动回收资源时,其实GC调用的是析构函数就像上面的~A(),但是C#在编译析构函数时,隐式地重载Finalize()代码,所以上述析构函数~A()等价于IL代码:
protected override void Finalize()
{
try
{
Console.WriteLine("GC had collected this object");
}
finally
{
base.Finalize();
}
}
3、非托管资源-IDisposable接口
GC对于托管资源能够很好的回收,那么非托管资源呢?如数据库链接,文件流,GC并不能自动回收这种对象(因为GC没法判断非托管资源何时需要回收),此时,我们可以通过IDisposable接口显示地回收非托管资源,MSDN给出的模板如下:
public class A:IDisposable
{
private bool m_disposed = false;//标识符,防止重复释放
public void Dispose()
{
//释放所有资源(托管+非托管)
Dispose(true);
//告知GC回收时忽略这个对象
GC.SuppressFinalize(this);
Console.WriteLine("回收所有资源");
}
protected virtual void Dispose(bool disposing)
{
if (!m_disposed)
{
if (disposing)
{
//释放托管资源
}
//释放非托管资源
m_disposed = true;
}
}
~A()
{
//释放非托管资源
Dispose(false);
Console.WriteLine("析构函数启动,回收非托管资源");
}
}
该模板保证了托管资源与非托管资源在何时应当被回收,一般与using语句一块使用,using(A a = new A()){}在作用域结束后,自动调用Dispose()函数,如Finalize()编译为IL语句一样,using语句自动添加try/finally语句块{}