【问题标题】:Why does my destructor never run?为什么我的析构函数从不运行?
【发布时间】:2011-08-03 18:23:58
【问题描述】:

我有一个带有析构方法的空白 Winform

public partial class Form1 : Form
{
    public Form1()
    {
        System.Diagnostics.Trace.WriteLine("Form1.Initialize " + this.GetHashCode().ToString());
        InitializeComponent();
    }
    ~Form1()
    {
        System.Diagnostics.Trace.WriteLine("Form1.Dispose " + this.GetHashCode().ToString());
    }
}

当表单被销毁时,我希望它写入输出窗口:

(Form1打开) Form1.初始化 41149443 (Form1 关闭) Form1.Dispose 41149443

MSDN 提出了 3 种实现析构函数的方法:

但是,这些方法都没有将“Form1.Dispose 41149443”写入输出窗口。 因此,我无法判断表格是否已被销毁。建议?

由于垃圾收集器的不确定性,我是否应该放弃实现这一目标的希望?

有没有其他方法可以知道 Form1 是否被垃圾回收了?

【问题讨论】:

  • 为什么?你想做什么?
  • 你不能假设析构函数会被调用。 (例如,如果你有足够的内存,那么永远不要调用任何析构函数是完全有效的。)
  • trace不是写了吗?
  • 输出跟踪没有写“Form1.Dispose 41149443”
  • 在应用程序终止之前是否刷新输出跟踪?否则,您的跟踪可能会在写入之前被垃圾收集。

标签: c# winforms garbage-collection


【解决方案1】:

您列出的实现析构函数的三种方法中只有一种实际上涉及析构函数,那就是~Destructor()

如果您实现IDisposable,并处置您的对象,那么Dispose 中的代码将运行,但没有理由认为您的析构函数会运行。

我认为你在这里追逐不可能的事情。析构函数按照垃圾收集器的命令运行。这不是你可以控制的东西。 GC 完全有权形成运行析构函数只是浪费时间的观点,如果内存充足,它就会形成这种观点。

如果您需要可预测的处置、最终确定等,请使用IDisposable

【讨论】:

  • IDisposable 不能自动预测,您必须调用 Dispose 或使用 using()。以及如何调用 Dispose()?
  • @slfan 你在说什么? Dispose 在被调用时运行,总是通过using。我在哪里说过Dispose 会自动运行?
  • 您写道“如果您需要可预测的处置......”。如果您不使用 using 或显式调用它,则不会调用它。因为我从未使用过表单,所以它比终结器更难预测。
  • 是的,它也不调用终结器!这就是为什么我不会称其为可预测的处置。这只是使用非托管资源的标准模式,任何具有终结器的类都应该实现 IDisposable 模式。但在我看来,这个答案不是对上述问题的回答。
  • @slfan 我什至在答案中说终结器不会运行。
【解决方案2】:

除非您正在研究垃圾收集,否则析构函数不是您跟踪的地方。您应该查看 Dispose(在 Form 中可覆盖)。这发生在释放非托管资源(如您的窗口句柄)之后。

protected override void Dispose(bool disposing)
{
   System.Diagnostics.Trace.WriteLine(
      "Form1.Dispose " + (disposing ? "disposing " : "")
      + this.GetHashCode().ToString());
   base.Dispose (disposing);
}

如果您想查看表单/控件是否已被释放,请使用Control.IsDisposed 属性。

编辑:由于GC.SuppressFinalize,如果显式(或由框架)调用 Dispose,您的 Finalize 方法(C# 中的析构函数语法)将永远不会执行。

有关详细信息,请参阅Implementing a Dispose Method

【讨论】:

  • @agent-j 不应该是"Form1.Dispose " + (disposing ? "disposing " : "")
  • @slfan,我的意思是只有在必要时才由终结器调用。如果有人显式地处理表单,那么由于 GC.SupressFinalize,析构函数(finilize 方法)可能永远不会被执行。有关详细信息,请参阅 Dispose 模式。
  • @agent-j:你说得对,我没有看到带bool参数的Dispose()实际上是在Form类中实现的。因此,您的 Dispose 将从终结器或基本方法中调用。但是你应该总是调用 base.Dispose(disposing);当您覆盖此方法时。
【解决方案3】:

是的,您可能应该放弃析构函数的想法,因为它们本质上是不确定的。我不确定为什么您需要将表单 disposed 而不是刚刚关闭,但在大多数情况下只需关闭它就足够了。

您可以使用IDisposable,但这取决于您需要对表单进行垃圾收集的原因。如果您需要重复使用它,只需创建另一个实例即可。

【讨论】:

    【解决方案4】:

    当不存在引用并且垃圾收集器碰巧运行时,表单会被垃圾收集。您可以通过调用 GC.Collect() 来强制垃圾收集器。 您不应该在终结器(又名析构函数)中引用任何其他对象,因为该对象可能已经被垃圾回收了。

    如果确实需要,您可以使用内存分析器工具来确定您的对象是否被垃圾回收。

    您还必须记住,终结器是从主线程以外的线程调用的。

    编辑: 如果您的问题只是看不到跟踪输出,则可能需要打开自动刷新

    <configuration>
      <system.diagnostics>
        <trace autoflush="true" />
      </system.diagnostics>
    </configuration>
    

    编辑 2: 可能有对您的表单的外部引用,例如已注册的事件处理程序。我建议您在应用程序的管理区域中添加一个按钮,该按钮执行以下代码:

    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    

    像这样垃圾收集器必须运行并且应该销毁你的表单(在终结器中设置一个断点)。如果没有,您对必须销毁的对象有一些引用。

    【讨论】:

    • 好的,试试我的第二个猜测。也许您对表单有一些参考。
    • 我尝试了第二个选项,它奏效了。与其他人相比,我认为您的答案是最接近的。
    【解决方案5】:

    向设计器添加一个 FormClosed 事件。

    即:

    this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(Form1_FormClosed);
    

    然后创建相应的函数来处理事件。

    【讨论】:

      【解决方案6】:

      您可以尝试将表单包装在 WeakReference 对象中,然后检查其 IsAlive 属性以确定它是否已被垃圾回收。但是,是的......根据其他答案,您最好离开它并信任 GC 来完成它的工作!

      【讨论】:

        猜你喜欢
        • 2010-11-21
        • 2019-09-03
        • 2010-12-21
        • 2018-02-27
        • 2015-01-30
        • 2015-08-21
        • 2021-12-09
        相关资源
        最近更新 更多