none
GC.Collect(), WeakReference.IsAlive 都不给力,有其他办法吗? RRS feed

  • 问题

  • 委托、事件的触发端,在触发时(如下文的:PostMessage()),无法判知当初的绑定者对象,是否已经无效、死亡。

    因此,有人提出WeakEventHandle的概念,与我下面的实现,比较相似。但,有一个问题都是没有解决的,GC的效率问题。先看代码:

        public class Apple
        {
            public Apple() { MessageDispatch.Instance.BindingMessage(0, OnMessage); }
            public void OnMessage(object sender, EventArgs e) { Console.WriteLine("Apple,thanks."); }        
        }
        public class MessageDispatch
        {
            public static readonly MessageDispatch Instance = new MessageDispatch();
            Dictionary<int, List<Pack>> _dic = new Dictionary<int, List<Pack>>();
    
            class Pack
            {
                public WeakReference WObject;
                public MethodInfo WEvent;
            }
    
            public void BindingMessage(int msgId, EventHandler e)
            {            
                if (!_dic.ContainsKey(msgId))
                    _dic[msgId] = new List<Pack>();
    
                Pack pack = new Pack(); //把消息放入:WeakReference队列
                pack.WObject = new WeakReference(e.Target);
                pack.WEvent = e.Method;
                _dic[msgId].Add(pack);
            }
    
            public void PostMessage(int msgId, Object sender, EventArgs data)
            {
                if (!_dic.ContainsKey(msgId))
                    return;
    
                //GC.Collect(); //重量级的庞然大物。
                foreach (Pack p in _dic[msgId]) { 
                    if (p.WObject.Target != null)
                        p.WEvent.Invoke(p.WObject.Target, new object[]{sender, data});
                }        
            }
        }
    
    测试一下:
    private void Test_One()
            {
                Apple a1 = new Apple();
                CreateTempVal();
                MessageDispatch.Instance.PostMessage(0, null, EventArgs.Empty);
            }
            private void CreateTempVal()
            {
                Apple a2 = new Apple();
            }

    输出:

    Apple,thanks.

    Apple,thanks.

    放开GC.Collect()的注释行后:

    Apple,thanks.

    代码中,我实现了对“a2”的有效性的实时判断。一旦a2脱离作用域(scope),a2就无法响应消息了。放开GC后即可实现。 现实中,时常遇到这种情况:对象绑一个事件,对象已经无效了,但,我们一旦触发事件,这个对象还会响应。这个代码就是要解决这个问题。

    一般情况下,采用GC回收,对系统的影响是很小的。 但是,如果我们的系统是庞大的系统,而且GC被循环调用、被MouseMove()调用。。。。。对GC来说,就是悲剧了,对于GC,无论我们用代,或者其他方式,均无法改变。

    我查阅了很多资料,试图做出改变,都是无功而返。 这里我始终有一个疑惑:GC.Collect()是如此庞大,为何微软不提供一个Peek(obj)的功能呢?里面只是查询当前obj是否已经被标示为垃圾,而不需要进行整体的资源回收,整体的vm对象调整,为什么?  

    呵呵,希望得到您的意见,谢谢。



    2015年1月12日 6:34

全部回复

  • 你好:

    不是太清楚你想表达的具体意思,你所说的“不给力”具体是指什么情况? “GC.Collect()是如此庞大” 是什么意思?

    一般情况下不建议使用GC.Collect()来强制进行垃圾回收,因为这个方法在执行垃圾回收的时候会阻塞线程的执行,导致程序性能下降。建议看一下这篇博客:

    http://blogs.msdn.com/b/ricom/archive/2004/11/29/271829.aspx

    关于若引用,也建议你参考一下MSDN文档:

    http://msdn.microsoft.com/en-us/library/system.weakreference(v=vs.110).aspx

    http://msdn.microsoft.com/en-us/library/ms404247(v=vs.110).aspx

    Guidelines for Using Weak   References

    Use long weak references only when necessary as the state of the   object is unpredictable after finalization.

    Avoid using weak references to small objects because the pointer   itself may be as large or larger.

    Avoid using weak references as an automatic solution to memory   management problems. Instead, develop an effective caching policy for   handling your application's objects.

    弱引用一般用于大对象的缓存,当弱引用封装了一个对象的实例之后,GC在自动或者主动垃圾回收的时候就有可能会回收该对象实例,如果对象已经被回收了,再次使用它的时候需要重新创建一个新的实例。检查弱引用所指向的对象是否为null就可以判断对象是否已经被回收。如果仅仅像你代码里面那样处理一个简单的Apple类,不建议使用弱引用。


    We are trying to better understand customer views on social support experience, so your participation in this interview project would be greatly appreciated if you have time. Thanks for helping make community forums a great place.
    Click HERE to participate the survey.

    2015年1月13日 2:27
    版主
  • 呵呵,谢谢回复。

    问题在于:如果在for循环中,不断调用PostMessage(),等同于不断调用GC.Collect(),这种情况下,是不能容忍的。

    简单来说,站在MessageDispatch立场看,每次PostMesssage()时,我们需要验证,每个响应者对象,都是有效的。例如:在Test_One()中: a1 是有效的,a2是无效。 若没有使用GC回收,a1 a2都被认为是有效的了,这是个错误。 怎么判断a2是无效的? 而且做到实时判断?  采用弱引用和GC.Colloect()可以实现,但为了做到“实时性”要求我们每次都调用GC.Collect()进行回收。 这么做确实能够实现“实时”判断a2的有效性,但是,问题在于:如果循环调用了这个判断了呢? 例如是这样:

    while(true)

    {

    Test_One(); // will calling GC.Collect()

    Thread.Sleep(100);

    }

    这对GC来说,就是灾难。

    当一个对象,离开作用域后(如:a2),我们如何获知?我只能依赖:GC.Collect() + WeakReference()的方式了。 但,如上所述:GC 太重了,项目中是无法承担这种开销的(循环调用方式)。

    还有其他方式解决这个问题吗?  

    我假想一下,如果GC提供一个类似Peek的功能,或者:bool val = GC.IsAlive(myObject);  就很好嘛,属于轻量级的,而且方便我们对特定对象,判知IsAlive,这个功能应该是很自然的,.Net里面为什么没有呢?

    呵呵,希望能得到大家的意见。谢谢。


    2015年1月13日 4:41