c# - 奇怪的析构函数行为,调用两次,但第一次跳过代码执行

标签 c# .net garbage-collection destructor

为了简化事情,假设我在析构函数内有简单的类定义

public class MyDisposeFinalize
{
    ~MyDisposeFinalize()
    {
        var breakPoint = string.Empty;
    }
}

Main 方法中,我首先创建 MyDisposeFinalize 类的实例,然后将 null 分配给我的实例并调用垃圾回收。

    static void Main(string[] args)
    {
        var myDispose = new MyDisposeFinalize();
        myDispose = null;
        GC.Collect();

        System.Console.ReadKey();
    }

我希望当终结线程处理终结队列时,我的析构函数将被调用一次。

事实上,当我在入口、内部和导出的析构函数方法中放置断点时,我的行为非常奇怪:

  1. 遇到入口断点
  2. 然后再次遇到入口断点
  3. 击中内部断点
  4. 遇到退出断点
  5. 然后再次遇到入口断点

第一个想法是两个不同的线程来执行析构函数方法,但是为什么其中一个线程会跳过方法代码并仅在第二次迭代时执行它?一般来说,为什么析构函数被调用两次?

最佳答案

如果您在 ILSpy 中打开代码,您将看到以下内容:

protected override void Finalize()
{
    try
    {
        string breakPoint = string.Empty;
    }
    finally
    {
        base.Finalize();
    }
}

我希望通过打破左大括号和右大括号,您会看到执行多个“代码行”,这些代码行链接到与左大括号和右大括号关联的代码行。即 trybase.Finalize() 调用。

在原始 IL 中,代码的结尾部分如下所示:

finally
{
    IL_000a: ldarg.0
    IL_000b: call instance void [mscorlib]System.Object::Finalize()
    IL_0010: nop
    IL_0011: endfinally
} //

它调用 Object::Finalize(),这可能是断点第二次停止的地方。

我不确定在那里放置断点的预期行为是什么,但我认为这就是您所看到的原因。

关于c# - 奇怪的析构函数行为,调用两次,但第一次跳过代码执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37879948/

相关文章:

c# - 单位插值的正确名称是什么以及如何实现它们?

java - 如何在年轻一代集合中找出 Activity 对象?

c# - 具有效果和触发器的图像模板

c# - 如何在 asp.net 中动态更改 html <a> 标记的可见性

c# - 有什么方法可以使 XmlSerializer 以定义的顺序输出 xml?

java - 为什么 CMS 在初始标记时会停止世界,但在扫描阶段不会停止世界?

java - 是否可以从其终结器跟踪对象,以检测其他对象的终结器对对象的意外复活?

c# - 在映射时向 Automapper 提供构造函数参数

c# - Ajax AutoCompleteExtender - 为什么此代码不起作用?

c# - C# JIT 编译器是否优化空值检查?