• C#的垃圾回收机制及弱引用


          在上一篇中,讨论了字符串常量的拘留池和不可变性;对于字符串变量,没有这个特性(或其他DotNet的非托管资源),当我们使用完后就要手动回收,即将变量的值指向null(p=null),然而堆内存中,那个没有任何变量引用的对象并没有立即回收(还占用一定量的堆内存),所以当我们要进行一个相当耗时且最好不要中断的操作时,最好调用垃圾回收,回收内存中的“垃圾”(没有变量引用的对象和非托管资源)以保证内存足够使用,这里提一下,所谓非托管资源,指的是非Dotnet开辟的资源,用完后再调用释放资源,如:数据库连接中的SqlConnection,SqlCommand等对象,用完要调用Dispose()释放,通常使用这类对象用using(),保证离开using范围,对象被回收。

       接下来,说一下垃圾回收机制(垃圾回收机制只回收托管堆中的内存资源),在DotNet中有“代”的概念(共3代),举例说明:

    同时,我们假设第0代大小为256M,第1代是512M,第2代是1G。程序执行一段时间后,定义了3个变量d、e、f,第1代中b指向null,但是第0代放不下,那么Dotnet会提升第0代中存活变量的代,结果如下:

    程序继续执行一段时间,变量C又指向null,同时定义3个变量g,h,i,DotNet看到第0代放不下,继续提升第0代变量的代同时将新变量放入第1代

    依次类推,直至3代的内存消耗殆尽,此时DotNet会提升3个代的存储空间,保证变量能放进去。直至提升代的空间后,仍放不下,此时就内存溢出,抛异常了。当我们开始就定义一个大对象---1G的对象),将被直接放入第2代。

    了解了代的概念,开始上代码:

    1  Person p = new Person() { ID = 1, Name = "张三", BirthDate = DateTime.Now };  //对象初始化器
    2             p = null;
    3             GC.Collect();
    4             Console.WriteLine(p.ID+"=="+p.Name+"=="+p.BirthDate);
    View Code

     GC.Collect()就是告诉DotNet,请垃圾回收一把。这时,DotNet会将程序暂停,保存变量的当前状态,进行垃圾回收,回收完毕,重新分配内存,让程序继续执行(因为程序会暂停执行,所以对性能会有影响,所以一般不建议经常调用GC.Collect()来执行垃圾回收),上面的代码会抛空引用异常。

         弱引用,指的是一个变量可以被垃圾回收了。这个对象不会用了,但是日后又担心会用,而且这个对象的创建非常耗时,我们就要用弱引用引用起来,需要注意的是,弱引用引用起来的对象是可以被垃圾回收的,但只要没被垃圾回收(能引用得到),就可以继续使用。

     1 Person p = new Person() { ID = 1, Name = "张三", BirthDate = DateTime.Now };  //对象初始化器
     2             
     3             WeakReference wReference = new WeakReference(p);        //将p对象弱引用起来
     4             p = null;
     5 
     6             GC.Collect();           //手动调用垃圾回收
     7             object o = wReference.Target;      
     8             if (o!=null&&wReference.IsAlive)
     9             {
    10                 Person p1 = o as Person;
    11                 Console.WriteLine(p1.ID + "==" + p1.Name + "==" + p1.BirthDate);
    12             }
    13             else
    14             {
    15                 Console.WriteLine("对象被垃圾回收了,请重新创建对象吧");
    16             }
    View Code

    运行可知。这是因为我们手动调用了GC.Collect(),把这句话注释掉,再运行,

    Person p = new Person() { ID = 1, Name = "张三", BirthDate = DateTime.Now };  //对象初始化器
                
                WeakReference wReference = new WeakReference(p);        //将p对象弱引用起来
                p = null;
    
                //GC.Collect();           //手动调用垃圾回收
                object o = wReference.Target;      
                if (o!=null&&wReference.IsAlive)
                {
                    Person p1 = o as Person;
                    Console.WriteLine(p1.ID + "==" + p1.Name + "==" + p1.BirthDate);
                }
                else
                {
                    Console.WriteLine("对象被垃圾回收了,请重新创建对象吧");
                }
    View Code

    运行结果如下:

    由此可见,虽然将p指向了null,但是因为还没被回收,我们通过弱引用(WeakReference)能找到它,便可继续使用。

    在使用弱引用时,要注意:

          一定是先object o = wReference.Target; 将对象强引用起来,再加判断,如果先判断wReference.Target != null,再执行object o = wReference.Target时正巧p被垃圾回收了,那么o就是null。

    弱引用的适用场景:

    1.创建一个对象非常耗时;

    2.这个对象不会用了(可以被垃圾回收),但是日后又担心会用;

    3.弱引用的对象也不是一定就存在,如果手动调用GC.Collect()或者正好被垃圾回收,也会引用不到,此时若要用,只能重新创建对象。

  • 相关阅读:
    (转)静态方法与实例方法
    使用C#和Excel进行报表开发(8)
    js千分位
    各种语言多态性比较
    中国互联网100个Web2.0网站名单
    HDU4405 期望
    HDU1266 字符串逆转
    POJ1087 DInic
    POJ1003 水~
    HDU4403 DFS
  • 原文地址:https://www.cnblogs.com/chens2865/p/3838787.html
Copyright © 2020-2023  润新知