在托管/非托管融合中苦苦挣扎并试图特别小心回调到托管代码中,我遇到了两个看似平等(直到今天)代理在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,因为代码不能更直接。
答案 0 :(得分:1)
Mono以比.net运行时更保守的方式扫描堆栈中的引用,因此在这种情况下可能会发生对象的错误引用或隐藏引用(在生成的中间代码/本地var中)由jit)。通常,您可以通过在单独的调用框架中分配对象来解决这些问题,但案例1仍然可以应用。除了上面的简单测试程序外,这几乎不是问题。