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()或者正好被垃圾回收,也会引用不到,此时若要用,只能重新创建对象。

原文地址:https://www.cnblogs.com/chens2865/p/3838787.html