zoukankan      html  css  js  c++  java
  • 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
    }
    
  • 相关阅读:
    LR常用函数汇总
    常用工具软件包下载地址
    MySQL分表操作的例子
    Redis性能优化之redis.cnf配置参数
    Redis监控之redis-live.conf配置
    Oracle中查询和定位数据库问题的SQL语句
    Oracle种常用性能监控SQL语句
    show processlist使用介绍
    MySQL流程控制和存储过程介绍
    MySQL字符集和排序介绍
  • 原文地址:https://www.cnblogs.com/AMzz/p/13565638.html
Copyright © 2011-2022 走看看