C#垃圾回收机制-GC

收集博客资料排版而成,非原创

C#资源类型

简单来说分为值类型和引用类型。前者是分配在栈上,并不需要GC回收;后者是分配在堆上,因此它的内存释放和回收需要通过GC来完成

  • 托管资源
    • 内存分配的资源
  • 非托管资源
    • Stream,数据库的连接,GDI+的相关对象,还有Com对象等

回收的复杂性

实际上对象有一个重要的特点导致无用对象判断的复杂性:对象间的相互引用!

如果没有相互引用,就可以通过“引用计数”这种简单高效的方式实现无用对象的判断,并实现实时回收。

正是由于相互引用的存在导致GC需要设计更为复杂的算法,这样带来的最大问题在于丧失了资源回收的实时性,而变成一种不确定的方式。

回收方法

.Net提供了三种方法,也是最常见的三种,大致如下:

  1. 析构函数,用于GC
  2. 继承IDisposable接口,实现Dispose方法;
  3. 提供Close方法。

CloseDispose这两种方法的区别在于,

调用完了对象的Close方法后,此对象有可能被重新进行使用;

而Dispose方法来说,此对象所占有的资源需要被标记为无用了,也就是此对象被销毁了,不能再被使用。

例如,常见SqlConnection这个类,当调用完Close方法后,可以通过Open重新打开数据库连接,当彻底不用这个对象了就可以调用Dispose方法来标记此对象无用,等待GC回收

  • 对于托管资源

    .Net所指的托管仅仅针对内存(托管资源)的资源托管,系统提供GC-Garbage Collector机制,而至于其他资源则需要手动进行释放。

    只要判定此对象或者其包含的子对象没有任何引用是有效的,那么系统就认为它是垃圾。

  • 对于非托管资源的释放,C#提供了两种方式:

    Finalizer:写法貌似C++的析构函数,本质上却相差甚远。

    • Finalizer是对象被GC回收之前调用的终结器,初衷是在这里释放非托管资源,但由于GC运行时机的不确定性,通常会导致非托管资源释放不及时。
    • 另外,Finalizer可能还会有意想不到的副作用,比如:被回收的对象已经没有被其他可用对象所引用,但Finalizer内部却把它重新变成可用,这就破坏了GC垃圾收集过程的原子性,增大了GC开销。

    Dispose Pattern:C#提供using关键字支持Dispose Pattern进行资源释放。

    • 这样能通过确定的方式释放非托管资源,而且using结构提供了异常安全性。
    • 所以,一般建议采用Dispose Pattern,并在Finalizer中辅以检查,如果忘记显式Dispose对象则在Finalizer中释放资源。

回收的过程

系统为GC安排了独立的线程。那么GC的工作大致是,查询内存中对象是否成为垃圾,然后对垃圾进行释放和回收。对于GC对于内存回收采取了一定的优先算法进行轮循回收内存资源。

其次,对于内存中的垃圾分为两种,一种是需要调用对象的析构函数,另一种是不需要调用的。GC对于前者的回收需要通过两步完成,第一步是调用对象的析构函数,第二步是回收内存,但是要注意这两步不是在GC一次轮循完成,即需要两次轮循;相对于后者,则只是回收内存而已。

GC为了提高回收的效率使用了Generation的概念,原理如下:

  1. 第一次回收之前创建的对象属于Generation 0,之后,每次回收时这个Generation的号码就会向后挪一,也就是说,第二次回收时原来的Generation 0变成了Generation 1,而在第一次回收后和第二次回收前创建的对象将属于Generation 0。
  2. GC会先试着在属于Generation 0的对象中回收,因为这些是最新的,所以最有可能会被回收,比如一些函数中的局部变量在退出函数时就没有引用了(可被回收)。
  3. 如果在Generation 0中回收了足够的内存,那么GC就不会再接着回收了如果回收的还不够,那么GC就试着在Generation 1里回收内存,以此类推

所以GC回收内存的机制不是即时回收,是内存中存在一定数量的垃圾之后,GC将进行内存回收,直到回收到足够数量内存为止

补充

托管资源可以通过调用GC.Collect();来强制GC进行垃圾回收

非托管资源

public class AA:IDisposable//继承IDisposable,从而获得接口Dispose
{
	FileStream fs = new FileStream("D://a.txt",FileMode.Open);
	~AA()
	{
		MessageBox.Show("析构函数被执行了");
	}

 	#region IDisposable 成员
	public void Dispose()
	{
		fs.Dispose();
		MessageBox.Show("dispose执行了");
		GC.SuppressFinalize(this);//告诉GC,让它不用再调用对象的析构函数,
	}
	#endregion
}
posted @ 2020-08-26 15:49  AMzz  阅读(546)  评论(0编辑  收藏  举报
//字体