目的
为了及时释放宝贵的非托管资源和托管资源,并且保证资源在被 gc 回收的时候可以正确释放资源,同时兼顾执行效率。
必须遵循的事实
1 . 托管资源释放:
由另一线程的 gc 进行释放,当托管的对象没有被引用时,就会在“适当的时候”进行回收。
如果定义了析构函数,回收的时候会调用析构函数(实际执行可能有差别),之后释放对象占用的内存。
当类有析构函数时, gc 会分分两步来释放,如果没有析构函数或者指定不需要调用析构函数时,只需要一步就能释放。
2. 非托管资源必须显式释放
方案
1. 把资源释放都放在析构函数里
可以保证资源都释放,但是由于 gc 调用时机的不确定性,导致宝贵的非托管资源无法及时释放。
2. 写个释放函数,手动是调用
如果忘了释放的话, 托管资源会被 gc 释放,但非托管资源就无法释放
3. Dispose 模式。参考下面的代码
手动调用Dispose() 可以释放所有资源,并且在 gc 标记不需要再调用析构函数,从而提高了效率。如果忘记调用Dispose(), 则当 gc 调用析构函数的时候也会把非托管资源释放掉。
参考代码
public interface IDisposable
{void Dispose();
}
public class DisposablClass : IDisposable
{//是否回收完毕bool _disposed;public void Dispose(){Dispose(true);GC.SuppressFinalize(this); //标记gc不在调用析构函数}~DisposableClass(){Dispose(false);}private void Dispose(bool disposing){if(_disposed) return; //如果已经被回收,就中断执行if(disposing){//TODO:释放本对象中管理的托管资源}//TODO:释放非托管资源_disposed = true;}
}
可能存在的疑问
既然 gc 是另外一线程执行的,为什么Dispose(bool)函数里不加锁?
因为如果可以主动调用的时候,肯定此对象不是死对象,也不会被回收,因此不会同时调用到哪里不太对,但又说不上来。
为什么析构函数调用的dispose(false)不释放托管资源?
因为析构函数由 gc 来调用,gc 会依次释放所有的死对象(不可到达),释放的顺序是随机的,如果在一个对象的析构里调用了一个本次 gc已经释放的对象,就会发生释放两次的错误。