编程语言
首页 > 编程语言> > c#-具有终结器的开销-在处理中有/没有SuppressFinalize

c#-具有终结器的开销-在处理中有/没有SuppressFinalize

作者:互联网

假设以下内容:

>班级仅管理成员.
>一些成员实现IDisposable.
>类是密封的-类不能从非派生资源派生和添加.
>该对象在using语句内使用-即完成后调用Dispose().

此类有3种可能的IDisposable实现:

>在IDisposable成员上调用Dispose()的最小Dispose方法-没有终结器.
>使用Finalizer BUT的标准IDisposable实现缺少常规的GC.SuppressFinalize(this)调用Dispose().
>使用Finalizer(以及Dispose()中的GC.SuppressFinalize(this)调用)的完整标准IDisposable实现.

以下陈述正确吗?我理解正确吗?

>案例A.的开销比B.和C.少,因为该对象没有终结器,因此它不会进入GC的终结队列中-因为GC可以在收集早期清理该对象-没有开销.
>案例B.该对象具有终结器,因此最终将进入GC终结器队列,并且终结器将被调用(因为未抑制该终结器)-终结器调用dispose不会执行任何操作,因为已被调用.这将导致对象处于终结器队列中的开销很小,并且终结器调用的开销也很小.
>案例C.该对象具有终结器,因此仍将最终出现在GC终结器队列中.因为dispose和SuppressFinalize已被调用,所以终结器不会运行.这种情况仍然会导致终结器队列中的对象开销很小,但是终结器实际上并没有运行.

这里的关键点是,很容易想到“我已经通过调用SuppressFinalize避免了终结器的开销”,但我认为(并想澄清)这是不正确的.终结器队列中仍存在对象的开销-您要避免的是实际的终结器调用-在通常情况下,这只是“我已经无所事事”.

注意:这里的“完全标准IDisposable实现”是指旨在涵盖非托管和托管资源情况的标准实现(请注意,这里我们只有托管对象成员).

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

private bool _disposed;
protected virtual void Dispose(bool disposing) {
if (_disposed)
    return;
    if (disposing) {
        // dispose managed members...
    }
    _disposed = true;
}

~AXCProcessingInputs() {
    Dispose(false);
}

解决方法:

.NET GC的不同版本可能会执行不同的操作,但是据我了解,具有Finalize方法的任何对象都将添加到“ finalizer队列”(如果被放弃则请求通知的对象列表),并将其保留在该队列中只要它存在.注销和重新注册终结的方法(IMHO应该是对象的受保护成员)设置或清除对象标头中的标志,该标志控制对象是否应移至“可到达队列”(其finalize方法应包含对象的列表)如果发现已放弃该对象,则将尽快运行),但不会导致该对象被添加到终结器队列中或从中删除.

因此,覆盖类型的每个实例的每个实例(如果存在)都会对它所参与的每个垃圾收集周期施加很小但非零的开销.在放弃对象之前在对象上调用SuppressFinalize可以防止将其移动到易碎队列中,但是不会消除由于对象一直存在于可终结队列中而导致的开销.

我建议不要将面向公众的对象实现为Finalize. Finalize方法有一些合法用途,但是覆盖它的类应避免保留对终结不需要的任何内容的引用(令人讨厌的是,如果finalizable对象持有对弱引用的唯一引用,则弱引用可能在终结器之前无效运行,即使弱引用的目标仍然有效).

标签:finalizer,idisposable,dispose,c,suppressfinalize
来源: https://codeday.me/bug/20191120/2045129.html