Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/flash/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# lambda事件处理程序会导致什么类型的内存泄漏?_C#_Events_Memory Leaks - Fatal编程技术网

C# lambda事件处理程序会导致什么类型的内存泄漏?

C# lambda事件处理程序会导致什么类型的内存泄漏?,c#,events,memory-leaks,C#,Events,Memory Leaks,事件处理程序很容易导致内存泄漏,因为事件的调用列表包含对事件处理实例的引用,因此如果事件源仍处于活动状态,则无法对事件处理实例进行垃圾收集 但请考虑下面的代码: public class SomeClass { public event EventHandler SomeEvent; } public class MyClass { public MyClass(SomeClass source) { //VERSION 1 source.

事件处理程序很容易导致内存泄漏,因为事件的调用列表包含对事件处理实例的引用,因此如果事件源仍处于活动状态,则无法对事件处理实例进行垃圾收集

但请考虑下面的代码:

public class SomeClass
{
    public event EventHandler SomeEvent;
}
public class MyClass
{
    public MyClass(SomeClass source)
    {
        //VERSION 1
        source.SomeEvent += OnSomeEvent;

        //VERSION 2
        void localHandler(object s, EventArgs args) { Console.WriteLine("some action with(out) any references"); }
        source.SomeEvent += localHandler;

        //VERSION 3
        var x = new object();
        source.SomeEvent += (s, e) => { Console.WriteLine("some event fired, using closure x:" + x.GetHashCode()); };

        //VERSION 4
        source.SomeEvent += (s, e) => { Console.WriteLine("some action without any references"); };
    }

    private void OnSomeEvent(object sender, EventArgs e) 
    {
        //...
    }
}
我的假设/问题:为什么不同的事件处理版本可能导致内存泄漏:

  • 版本1:因为调用目标明确引用了
    MyClass
    的实例
  • 版本2:因为对
    localHandler
    的引用意味着对
    MyClass
    实例的引用-除非
    localHandler
    中的代码没有对
    MyClass
    实例的引用
  • 版本3:因为lambda包含一个闭包,闭包本身就是对
    MyClass
    实例的引用,或者它是
  • 版本4:因为lambda没有引用
    MyClass
    的实例,这可能不会导致泄漏
以及第3版和第4版的后续问题:

  • .Net为lambda/closure创建的“magic helper对象”存储在哪里?它是否(始终)包含一个将保持
    MyClass
    实例活动的引用
  • 如果lambda事件处理程序可能泄漏,则它们应仅在不存在问题的情况下使用(例如,
    MyClass
    实例超过
    SomeClass
    实例),因为无法使用
    -=
    删除它们

编辑:这篇文章(最初的标题是“什么时候事件处理程序会导致内存泄漏?”)被认为是重复的,但我不同意,因为这个问题是专门针对lambda事件处理程序的。我重新表述了问题/标题,使之更清楚。

免责声明: 我不能保证这是100%的事实-你的问题很深,我可能会犯错误

然而,我希望它能给你一些想法或方向

让我们考虑这个问题,根据代码> CLR < /COD>内存组织:

本地方法变量和方法参数存储在内存中的方法堆栈框架中(除非它们用
ref
关键字声明)

堆栈存储指向堆中对象的值类型和引用类型变量引用

方法执行时存在方法堆栈帧,方法结束后局部方法变量将随堆栈帧一起出现

除了以某种方式捕获局部变量外,它还与编译器的工作有关,您可以在Jon Skeet的网站上阅读:

Version 1
OnSomeEvent
方法是
MyClass
的成员,它将被
Someclass source
实例捕获,直到引用此方法的委托不会从事件中移除。因此,
GC
不会收集在构造函数中创建的、放置在堆中并保存此方法的
MyClass
实例,直到它的方法引用不会从事件中删除

编译器以特定方式编译lambda,请完整阅读实现示例段落:

第4版: 我提供的2个链接将把lambda编译成
MyClass
方法,它将被
SomeClass
实例捕获,如版本1所示

第2版: 我不知道如何编译本地方法的细微差别,但它应该与第4版(因此,第1版)相同

第3版: 所有局部变量都将通过有趣的方式捕获

您还有“object x”,因此将创建编译器生成的类,这 将包含公共字段<代码>公共对象x和将从lambda转换的方法(参见实现示例段落)

因此,我认为在版本1、2、4中,内部相同: MyClass将包含将用作事件处理程序的方法

版本3中,将创建编译器生成的类,它将保存从lamdba翻译过来的局部变量和方法

SomeClass
事件的方法位于调用列表中之前,GC不会收集任何类的任何实例

  • 我认为您指的“magic helper对象”是编译器生成的类实例
  • 您可以通过将null分配给事件来删除订阅:
    source.SomeEvent=null

我不认为它是重复的,因为我的问题超出了你的链接中的一个,特别要求LAMBDAS作为事件处理程序。谢谢你精心的反应。我猜关键语句是“任何类的任何实例都不会被GC收集,直到SomeClass事件在调用列表中有它的方法。”。而且,多亏了你的解释,也很清楚哪个实例被泄露了。@mike,不客气。是的,这是关键陈述,你也可以在你的问题下的第一条评论中的链接提供的答案中阅读。