Dispose() 方法中 GC.SuppressFinalize(this) 的目的是什么?

发布于 2024-09-05 14:18:14 字数 483 浏览 4 评论 0原文

我有以下代码:

public void Dispose()
{
    if (_instance != null)
    {
        _instance = null;
        // Call GC.SupressFinalize to take this object off the finalization
        // queue and prevent finalization code for this object from
        // executing a second time.
        GC.SuppressFinalize(this);
    }
}

虽然有一条注释解释了与 GC 相关的调用的目的,但我仍然不明白为什么它在那里。

一旦所有实例不再存在,例如在 using 块中使用时,对象是否注定要进行垃圾回收?

这将在什么用例场景中发挥重要作用?

I have the following code:

public void Dispose()
{
    if (_instance != null)
    {
        _instance = null;
        // Call GC.SupressFinalize to take this object off the finalization
        // queue and prevent finalization code for this object from
        // executing a second time.
        GC.SuppressFinalize(this);
    }
}

Although there is a comment that explains purpose of that GC-related call, I still don't understand why it's there.

Isn't object destined for garbage collection once all instances cease from existence, like, when used in using block?

What's the use case scenario where this would play important role?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

装迷糊 2024-09-12 14:18:15

垃圾回收:当对象不再被引用时,GC会回收该对象使用的内存。

Dispose:IDisposable 接口中的一个方法,当程序员调用它时(直接或通过 using 块间接调用),它会释放所有托管和非托管资源。

Finalizer:释放所有非托管资源的方法。在回收内存之前由 GC 调用。

托管资源:任何实现IDisposable接口的.NET类,例如Streams和DbConnections。

非托管资源:托管资源类中包装的内容。 Windows 句柄是最简单的例子。


现在回答你的问题:

GC 保留所有对象的列表(终结队列),这些对象的类声明了终结器(C# 中的~ClassName)。对象在创建时就放入此队列中。 GC 会定期运行以检查是否存在程序无法访问的对象。然后,它检查是否从 Finalization Queue 引用了任何不可访问的对象,并将它们放入另一个称为 Freacheable 队列的队列中,而其余的则被回收。一个单独的线程用于运行 Freacheable 队列中对象的 Finalize 方法。

下次GC运行时,它会发现之前在Freacheable队列中的一些对象已经Finalized,可以回收了。请注意,GC 需要至少两个周期(如果需要执行大量 Finalization,则需要更多周期)才能使用 Finalizer 清除对象,这会导致一些性能损失。

SuppressFinalize 方法只是在对象标头中设置一个标志,指示不必运行 Finalizer。这样GC就可以立即回收对象的内存。根据上面的定义,Dispose 方法与 Finalizer 执行相同的操作(甚至更多),因此如果执行它,则不再需要 Finalization。使用 SuppressFinalize 方法,您可以通过通知 GC 这一事实来为 GC 节省一些工作。此外,现在您不必在 Finalizer 中实现检查以避免双重释放。 Dispose 的唯一问题是它不能保证运行,因为调用它是程序员的责任,这就是为什么有时我们需要费心使用终结器。


话虽如此,您需要编写 Finalizer 的情况非常罕见,因为对于绝大多数常见的非托管资源来说,托管包装器已经存在,并且托管资源将通过调用其 DisposeDispose 方法的 code> 方法,并且仅从那里开始!在终结器中,您绝不能调用 Dispose 方法。


进一步阅读

Garbage collection: GC reclaims the memory used by the object when the object is referenced no more.

Dispose: a method from the IDisposable interface that is to release all managed and unmanaged resources when the programmer calls it (either directly or indirectly via a using block).

Finalizer: a method to release all unmanaged resources. Called by the GC before reclaiming the memory.

Managed resource: any .NET class that implements the IDisposable interface, like Streams and DbConnections.

Unmanaged resource: the stuffing wrapped in the Managed Resource classes. Windows handles are the most trivial examples.


Now to answer your question:

GC keeps a list (Finalization Queue) of all objects the class of which declares a Finalizer (~ClassName in C#). Objects are put in this queue upon creation. GC runs periodically to check whether there are any objects inaccessible from the program. It then checks if any of the inaccessible objects are referenced from the Finalization Queue, and puts these in another queue called the Freacheable queue, while the rest are reclaimed. A separate thread is used to run the Finalize methods of objects in the Freacheable queue.

The next time GC runs, it will find that some of the objects previously in the Freacheable queue are already Finalized thus are ready for reclaiming. Note that GC needs at least two cycles (or a lot more if there is a lot of Finalization to do) to get rid of an object with a Finalizer, which incurs some performance penalties.

The SuppressFinalize method simply sets a flag in the object header which indicates that the Finalizer does not have to be run. This way GC can reclaim the memory of the object right away. As per the definition above, the Dispose method does the same thing as the Finalizer (and more), so if it is executed then Finalization is not neccessary any more. Using the SuppressFinalize method, you can save some work for the GC by notifying it about this fact. Additionally, now you don't have to implement checks in the Finalizer to avoid double releasing. The only problem with Dispose is that is is not guaranteed to run, because it is the programmer's responsibility to call it, that is why sometimes we need to bother with Finalizers.


That being said, it is only very very rarely that you need to write a Finalizer, because for the vast majority of the usual unmanaged resources a managed wrapper already exists, and managed resources are to be released by calling their Dispose methods from your own Dispose method, and from there only! In finalizers you must never call a Dispose method.


Further reading:

天煞孤星 2024-09-12 14:18:15

可以最终确定的对象会在第一次 GC 运行后存活下来。

通常,当 GC 检测到某个对象无法访问时,就会回收该对象。如果该对象是可终结的,那么 GC 不会回收它;相反,它认为它仍然是可访问的(以及该对象引用的所有对象,等等),并安排它进行终结。仅当在完成后的某个时刻再次发现该对象无法访问时,该对象才会被回收。

这意味着可终结的对象会产生额外的成本:该对象必须在内存中保留更长时间。因此,您看到的调用是:在不需要时抑制终结是值得的。在这里,对象使用终结来确保它总是在某个时刻被“处置”。当它被显式处理时,就不再需要最终确定。

Objects which can be finalized survive the first GC run.

Normally, when the GC detects that an object is unreachable, then it reclaims it. If the object is finalizable, then the GC does not reclaim it; instead, it considers it reachable nonetheless (and all the objects that this object references, and so on), and schedules it for finalization. The object will be reclaimed only when it is find again to be unreachable at some point after it has been finalized.

This means that a finalizable object incurs an extra cost: the object must be kept around in memory for a longer time. Hence the call you see: it is worthwhile to suppress finalization when it is not needed. Here, the object uses finalization to ensure that it is always "disposed of" at some point. When it is disposed explicitly, it needs not be finalized anymore.

梦里°也失望 2024-09-12 14:18:15

如果您的类型实现了终结器 (~MyType() { }),它会阻止垃圾收集器运行它。当您的终结器处理非托管类型,但用户已经调用 Dispose()(显式调用或通过 using() { } 块)时使用,释放那些非托管类型类型。

If your type implements a finalizer (~MyType() { }), it keeps the garbage collector from running it. Used when your finalizer takes care of unmanaged types, but the user has already called Dispose() (either explicitly or via a using() { } block), freeing those unmanaged types.

狼亦尘 2024-09-12 14:18:15

来自 MSDN:GC.SuppressFinalize

该方法在对象中设置一个位
标头,系统在以下情况时检查该标头
调用终结器。 obj 参数
必须是这个调用者
方法。

实现 IDisposable 的对象
接口可以调用这个方法
IDisposable..::.Dispose 方法
防止垃圾收集器
调用 Object..::.Finalize
不需要它的对象。

通常,如果您的对象不引用其他对象,而只是离散类型,或者已将任何对象引用重置为 NULL,则通常会使用此选项。

From MSDN: GC.SuppressFinalize:

This method sets a bit in the object
header, which the system checks when
calling finalizers. The obj parameter
is required to be the caller of this
method.

Objects that implement the IDisposable
interface can call this method from
the IDisposable..::.Dispose method to
prevent the garbage collector from
calling Object..::.Finalize on an
object that does not require it.

Typically you would use this if your object does not reference other objects, just discrete types, or has already reset any object references to NULL.

删除→记忆 2024-09-12 14:18:14

实现 dispose 模式时,您还可以向类中添加一个调用 Dispose() 的终结器。这是为了确保 Dispose() 始终 被调用,即使客户端忘记调用它。

要防止 dispose 方法运行两次(如果对象已被释放),请添加 GC.SuppressFinalize(this);。该文档提供了示例

class MyResource : IDisposable
{
    [...]

    // This destructor will run only if the Dispose method 
    // does not get called.
    ~MyResource()      
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue 
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if(!this.disposed)
        {
            // If disposing equals true, dispose all managed 
            // and unmanaged resources.
            if(disposing)
            {
                // Dispose managed resources.
                component.Dispose();
            }

            // Call the appropriate methods to clean up 
            // unmanaged resources here.
            resource.Cleanup()          
        }
        disposed = true;         
    }
}

When implementing the dispose pattern you might also add a finalizer to your class that calls Dispose(). This is to make sure that Dispose() always gets called, even if a client forgets to call it.

To prevent the dispose method from running twice (in case the object already has been disposed) you add GC.SuppressFinalize(this);. The documentation provides a sample:

class MyResource : IDisposable
{
    [...]

    // This destructor will run only if the Dispose method 
    // does not get called.
    ~MyResource()      
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    public void Dispose()
    {
        Dispose(true);
        // This object will be cleaned up by the Dispose method.
        // Therefore, you should call GC.SupressFinalize to
        // take this object off the finalization queue 
        // and prevent finalization code for this object
        // from executing a second time.
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        // Check to see if Dispose has already been called.
        if(!this.disposed)
        {
            // If disposing equals true, dispose all managed 
            // and unmanaged resources.
            if(disposing)
            {
                // Dispose managed resources.
                component.Dispose();
            }

            // Call the appropriate methods to clean up 
            // unmanaged resources here.
            resource.Cleanup()          
        }
        disposed = true;         
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文