弱引用应用的注意点

1.弱引用的基础介绍

    在某些场合,例如缓存某些大数据对象的时候,会遇到内存与时间的两难境况,如果让大对象过快的过期,那么每次创建对象会消耗过多的性能,反之,保持了过多的大对象,那么内存将耗尽,反而降低速度。

    此时,.net BCL中的弱引用(WeakReference)就出场了,如果内存尚且足够,那么GC就不会回收大对象占用的内存,那么弱引用就是可到达的,也就是说可以重用这个对象,达到缓存的目的。如果内存不足,那么GC就会不得不去回收那些大对象,从而释放内存空间。

    当然,要释放这些大对象的前提是没有任何可到达的引用(之后将成其为强引用,以强调与弱引用的区别)。换而言之,就是弱引用本身并不影响GC回收对象。

2.引出问题

    来看看这段程序:

        static void Main(string[] args)
        {
            WeakReference wr = GetWR();
            while (wr.IsAlive)
            {
                Thread.Sleep(100);
            }
            Console.WriteLine("OK");
            Console.ReadLine();
        }

        private static WeakReference GetWR()
        {
            object o = new object();
            WeakReference wr = new WeakReference(o);
            return wr;
        }

    看起来像那么回事,通过GetWR方法,获得了一个对象的弱引用,退出方法后,那么对这个对象的所有强引用就没了,理论上,wr.IsAlive将在某个时间变成false,也就是对象被GC回收了。但是运行起来,却发现,根本不会打出“OK”,为啥?因为没有新的对象被创建出来,GC一直认为不需要回收,然后,就一直是一个看起来不是死循环的死循环了,直到哪天GC大发慈悲,回收一把。

    那么这段代码怎么修改才能正确执行哪?不就是GC不肯回收嘛,强制回收就可以了,最简单的就是加上一句GC.Collect(),这样这个循环就能在适当的时候退出了。

3.弊端

    前面通过GC.Collect()方法解决了死循环问题,不过,又牵涉到另一个问题:GC.Collect()本身是一个比较消耗的操作,如果每0.1秒来一次全代回收,小程序是不要紧,但是服务类的程序可就不行了,这个性能会被拖慢,而且,全回收也会破坏那些企望尽量长时间停留在内存的大对象的驻留(也就是一开始说的正确的运用方式),因此弊端也不小。因此,在使用弱引用应该尽量避免这类应用:

  • 在前台线程使用循环并且退出条件是某弱引用不可到达

    如果使用这种方式,那么在整个应用程序结束时,需要所有的前台线程完成,而如果其中的一个前台线程出现了类似while(wr.IsAlive)的代码,就需要等待GC将被弱引用的对象回收,然而,GC的回收通常是不受控制的(可能很快,也可能需要很久),也就是说,这个前台线程可能会存活相当长的时间,并且由于是前台线程,以至于进程也会存活这样长的时间(对于用户而言,就是程序关不掉)。

PS:用了好久Paste As VS Code插件,今天终于发现了一个弊端,Host OS(win2003)上的VS中复制的代码无法用这个插件贴到Guest OS(win 7 in Virtual Box)上,当然这个也不能算缺陷,只能说是虚拟机应用上的一个小小的遗憾吧。

原文地址:https://www.cnblogs.com/vwxyzh/p/1679769.html