看似平等的代表改变了Mono的定稿

时间:2014-02-22 08:45:41

标签: c# mono

在托管/非托管融合中苦苦挣扎并试图特别小心回调到托管代码中,我遇到了两个看似平等(直到今天)代理在Mono中干扰GC的奇怪区别。为了简化故事,这里有一段代码片段:

using System;

class A {
    public Action f;

    ~A() {
        Console.WriteLine("~A");
    }
}

class Program {
    static void Main() {
        var a = new A();
        Action f = delegate {}; // Instantiating a delegate
        a.f = f; // Assigning the delegate
        a = null;

        Console.WriteLine("GC start");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        // 'a' has been finalized here
        Console.WriteLine("GC done");
    }
}

我得到了预期的输出:

GC start
~A
GC done

但是,如果我将实例化并将委托分配给一个的两行折叠,即取消使用显式局部变量:

        var a = new A();
        a.f = delegate {}; // Instantiating and assigning a delegate
        a = null;

        Console.WriteLine("GC start");
        GC.Collect();
        GC.WaitForPendingFinalizers();
        // 'a' has NOT been finalized here
        Console.WriteLine("GC done");

我在Mono 3.2.4中得到意外的输出:

GC start
GC done
~A

所以看起来第二种形式(不使用显式变量)意味着在某处对对象进行额外引用,而第一种形式使用显式局部变量,因此在技术上更容易受到观察到的异常的影响。

我在Windows上检查了相同的EXE,它按预期工作。现在有点困惑,无论是Mono中的细微差别还是bug,因为代码不能更直接。

1 个答案:

答案 0 :(得分:1)

Mono以比.net运行时更保守的方式扫描堆栈中的引用,因此在这种情况下可能会发生对象的错误引用或隐藏引用(在生成的中间代码/本地var中)由jit)。通常,您可以通过在单独的调用框架中分配对象来解决这些问题,但案例1仍然可以应用。除了上面的简单测试程序外,这几乎不是问题。