为什么用空lambda函数注册事件会导致内存中的强引用

本文关键字:内存 引用 lambda 函数 事件 注册 为什么 | 更新日期: 2023-09-27 18:12:08

有一些示例代码:

public class Publisher
    event Published()
end class
public class Subscriber
    public sub new(Publisher as Publisher)
        addhandler Publisher.Published,
            sub()
            end sub
    end sub
end class
  1. 注册到Subscriber构造器中的Publisher事件是否阻止Subscriber被垃圾收集,即使一个空lambda函数,不捕获Subscriber,作为事件处理程序传递?

  2. 在c#中是一样的吗?

编辑

一个简单的控制台应用程序来测试它:
Sub Main()
    Dim Publisher As New Publisher
    For i = 1 To 1000000
        Dim Subscriber As New Subscriber(Publisher)
    Next
    GC.Collect()
    Dim TotalMemory = GC.GetTotalMemory(True)
    Trace.WriteLine(TotalMemory)
End Sub
不幸的是,应用程序在垃圾收集之后消耗了36mb的内存,这意味着所有订户都留在内存中!

在不同的目标框架2.0、3.0、3.5、4.0、4.5上调试和发布,得到相同的结果。

  • 任何人都可以重现问题或解释为什么添加事件处理程序的方式?
  • 为什么用空lambda函数注册事件会导致内存中的强引用

    如果您在匿名函数中没有引用任何与订阅者相关的内容,那么它将被编译为订阅者类的静态方法,因此它不会阻止订阅者类的任何实例被垃圾收集。你可能想看看这个链接- http://blogs.msdn.com/b/oldnewthing/archive/2006/08/02/686456.aspx -了解更多信息。

    更新。下面的代码显示了在发布模式下运行时几乎相同的总内存值(在调试模式下,它确实像你说的那样工作):

    internal class Program {
        private static void Main(string[] args) {
            Console.WriteLine("Before: {0}", GC.GetTotalMemory(true));
            var pub = new Publisher();
            for (int i = 0; i < 1000000; i++) {
                var sub = new Subscriber(pub);
            }
            GC.Collect();
            Console.WriteLine("After: {0}", GC.GetTotalMemory(true));
            Console.ReadKey();
        }
    }
    public class Publisher {
        public EventHandler Published;
    }
    public class Subscriber {
        public Subscriber(Publisher pub) {
            pub.Published += delegate { };
        }        
    }