none
关于注册事件导致的内存泄露的问题, 麻烦大牛解惑 RRS feed

  • 问题

  • 用了这么久c#, 一直被其自动回收蛊惑, 没怎么注意对象回收的问题

    今天看了一个贴子说注册事件导致对对象的强引用, 会导致该对象没法被gc回收

     

    例如我当前在做一个即时消息的模块, 聊天窗口管理器会对所有打开的聊天窗口注册一个closed的事件, 以便在用户关闭聊天窗口时在内部列表中移去该窗口的引用, 以我一向的理解, 该窗口对象会被gc自动回收, 可现在看来, 我没有对该对象进行closed事件注销的操作, 会导致该对象一直存在于内存中.

    那么:

    我上面的理解是否有误?

    windowform或wpf有否对这些事件进行特殊的处理以解决这样的问题?

    2011年3月15日 1:41

答案

  • 是的,一个对象中的某些事件如果没有被注销的话,因为事件通常是一种强引用,所以GC会认为这个引用关系存在并且此对象不能被回收。所以一般的做法是在Closed事件的处理函数中显式地 "-=" remove 他的处理函数。关于这个泄露问题,WPF性能测试官方blog有篇文章中提到了事件泄露的内容:http://blogs.msdn.com/b/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx  里面的第一种情况: 1. Use of Event Handler

     

    然后,我想推荐你的是一种可以防止事件注册泄露的模式,弱引用事件模式: http://msdn.microsoft.com/en-us/library/aa970850.aspx 这样子的话,通过弱引用,即使事件处理函数没有被remove,GC也会认为是可以被回收的。Josh Smith的MVVM foundation项目中就实现了一个简单基本的Weak Event模式的类,你可以参考下。 http://mvvmfoundation.codeplex.com

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年3月15日 10:28
    版主
  • .Net 的事件是基于委托实现的,而委托中保存的MethodInfor对象,也就是这个处理函数的信息是以强引用形式存在的。(我们平时所有的.Net对象其实都是强引用形式存在,MS在一般情况下是不会为了特殊的领域而去改变设计,让某些技术默认就实现弱引用的。当然MS考虑到这一点,提供了WeakReference 类,这样,开发人员就可以自己去实现弱引用模式了)关于弱引用,MSDN有详细说明:http://msdn.microsoft.com/zh-cn/library/ms404247.aspx

    对于那篇文章中提到的事件注册泄露问题,我想补充的是,一个对象的事件如果被注册上了其它对象的方法,比如A.Event += B.Method; 那么在A生命周期结束时,如果B还没有结束,那么GC就会认为A.Event存在强引用,A不能回收。假如A.Event只是被注册了自己的方法,那么A自己在结束时,是不受影响的。事件上的处理方法会被GC,而导致A.Event上的Method变成null,从而A可以被回收。

     


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年3月16日 3:05
    版主

全部回复

  • 是的,一个对象中的某些事件如果没有被注销的话,因为事件通常是一种强引用,所以GC会认为这个引用关系存在并且此对象不能被回收。所以一般的做法是在Closed事件的处理函数中显式地 "-=" remove 他的处理函数。关于这个泄露问题,WPF性能测试官方blog有篇文章中提到了事件泄露的内容:http://blogs.msdn.com/b/jgoldb/archive/2008/02/04/finding-memory-leaks-in-wpf-based-applications.aspx  里面的第一种情况: 1. Use of Event Handler

     

    然后,我想推荐你的是一种可以防止事件注册泄露的模式,弱引用事件模式: http://msdn.microsoft.com/en-us/library/aa970850.aspx 这样子的话,通过弱引用,即使事件处理函数没有被remove,GC也会认为是可以被回收的。Josh Smith的MVVM foundation项目中就实现了一个简单基本的Weak Event模式的类,你可以参考下。 http://mvvmfoundation.codeplex.com

    Sincerely,


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年3月15日 10:28
    版主
  • 既然有解决之道, 为什么ms不在wpf及winfrom的事件机制里采用这种弱引用事件模型呢?

    现在这样消费客户端居然制约了对象的生存期, 是不是非常违反面向对象呢?

    2011年3月16日 1:30
  • .Net 的事件是基于委托实现的,而委托中保存的MethodInfor对象,也就是这个处理函数的信息是以强引用形式存在的。(我们平时所有的.Net对象其实都是强引用形式存在,MS在一般情况下是不会为了特殊的领域而去改变设计,让某些技术默认就实现弱引用的。当然MS考虑到这一点,提供了WeakReference 类,这样,开发人员就可以自己去实现弱引用模式了)关于弱引用,MSDN有详细说明:http://msdn.microsoft.com/zh-cn/library/ms404247.aspx

    对于那篇文章中提到的事件注册泄露问题,我想补充的是,一个对象的事件如果被注册上了其它对象的方法,比如A.Event += B.Method; 那么在A生命周期结束时,如果B还没有结束,那么GC就会认为A.Event存在强引用,A不能回收。假如A.Event只是被注册了自己的方法,那么A自己在结束时,是不受影响的。事件上的处理方法会被GC,而导致A.Event上的Method变成null,从而A可以被回收。

     


    Bob Bao [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2011年3月16日 3:05
    版主