【问题标题】:Do we even need a Finalizer method in .NET?我们甚至需要.NET 中的终结器方法吗?
【发布时间】:2012-11-16 15:19:06
【问题描述】:

我的班级正在使用 COM 资源,并且我从 IDisposable 继承了它。为了清理非托管资源,我有调用 Marshall.ReleaseCOMObject() 方法的 Dispose() 方法......所以我的问题是我们还需要 Destructor 方法(Finalizer)吗?释放COM资源?当我确实有 finalzier 方法并在其中调用 Marshal 或 Dispose 时,我会收到一个 RCW 错误,当我删除 finalizer 方法时,我不会再收到 RCW 错误但是如果我查看内存分析器,我可以看到非托管资源每次打开包含此 COM 资源的表单时,内存都会不断增加...所以看起来它没有清理它?那么在 C# 应用程序中清理 COM 资源的正确方法是什么?

我将使用我很快拥有的一些代码示例来更新问题。

谢谢。

private MyCOMobject theCOMobject = null;

static SuppressFieldCntrlr()
{
    new SomeCalss();
}


private bool disposed = false;

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
    if (!this.disposed)
    {
        if (disposing)
        {
           //nothing in here, is it just for Managed Resources? 
        }

        Marshal.ReleaseComObject(this);
        MethodFoo(false);

        disposed = true;
    }
}

【问题讨论】:

  • 只是想补充一点:如果你在你的类中添加终结器,你的类会自动处理一些不同的东西。 gc 的第一波,你的对象不是直接收集的,而是放在终结器队列中的。在下一次运行中,将被调用并收集。但是当然,这只发生在你的对象不再被任何引用持有的情况下,否则你可能只是有一个普通的内存泄漏,我猜这是你的问题。
  • @dowhilefor :有趣的信息。我不知道排队。普通的内存泄漏听起来是对的。您能否详细说明如何修复和找到它?这是关于此的原始问题,尚未回答:stackoverflow.com/questions/13609098/… 谢谢
  • 嗯,我最好的建议是:使用内存分析器来查找泄漏 :) 如果更喜欢 redgates 蚂蚁内存分析器,但我敢肯定那里有很多。一些调查建议:请记住,事件包含硬引用。检查你是否真的使用单例,检查所有在某一时刻持有引用并且比你的泄漏类寿命更长的对象。
  • @dowhilefor : 是的,我有 redgate...而且类构造函数是 STATIC...所以它只被调用一次...Smelly?

标签: c# winforms memory idisposable


【解决方案1】:

如果我们有非托管资源,强烈建议使用终结器。 在这种情况下:

~ClassName
{
    Dispose(false);
}

【讨论】:

  • 我正要提到这一点,但你说了一些关于异常的事情。这就是你的终结器的样子吗?
  • @Alan :是的,这正是我一开始所拥有的。该代码未回答的原始问题在这里:stackoverflow.com/questions/13609098/…
【解决方案2】:

如果您的类不直接拥有非托管资源,并且您知道没有派生类将直接拥有非托管资源(例如,因为您的类是密封的,您的类是内部的),那么您不需要终结者。

我个人会使用终结器来实现标准模式,即使它不是绝对必要的。主要是出于最不意外的原则,并且因为其他人可能决定在未来解封它或允许派生类使用非托管资源。

还要注意,COM 对象是一个托管资源,所以你应该在 disposing 为 true 时调用 ReleaseComObject:

private void Dispose(bool disposing)
{
    if (!this.disposed)
    {
        if (disposing)
        {
            // Dispose managed resources
            Marshal.ReleaseComObject(this);
        }
        // If you directly own unmanaged resources, they'd be freed here

        disposed = true;
    }
}

【讨论】:

  • 我认为,如果一个类没有被明确设计为处理非托管资源,那么派生类也不应该直接持有任何类。相反,如果派生类需要拥有非托管资源,则该资源应封装在旨在确保其清理的小型(可能是私有)类中,并且派生类应将该资源存储在该较小类的对象中。资源持有者对象需要终结器,但派生类不需要。 .net IDisposable 模式的存在是为了允许派生类做一些它们无论如何都不应该做的事情。
【解决方案3】:

您封装的是托管对象,而不是非托管对象。您还没有 P/invoke'd 来获取 Win32 文件句柄、设备上下文或类似的东西;相反,您获得了对托管对象(RCW)的引用。您应该在Dispose(bool) 方法的if (disposing) 部分中致电Marshal.ReleaseComObject

RCW 本身将有一个终结器,它将负责实际 COM 对象的 IUnknown->Release() 簿记,因此如果您尚未在需要进行终结时调用 ReleaseComObject,COM 对象将被垃圾收集器删除。

【讨论】:

  • 好的,谢谢,那么我现在应该删除 Finalizer 方法吗?现在将尝试它,还将查看内存分析器结果。我担心在没有终结器的情况下,非托管部分会变得越来越大。但是我现在肯定会尝试)。
  • 从您所展示的内容来看,您的课程中没有任何需要完成的内容,因此我将其删除。
猜你喜欢
  • 1970-01-01
  • 2021-06-27
  • 2010-09-20
  • 1970-01-01
  • 2021-11-30
  • 1970-01-01
  • 2016-09-24
相关资源
最近更新 更多