3

以下を仮定します。

  • クラスはメンバーのみを管理しています。
  • 一部のメンバーは実装しIDisposableます。
  • クラスはsealed- クラスはアンマネージ リソースから派生したり、追加したりすることはできません。
  • オブジェクトはusingステートメント内で使用されます。つまりDispose()、完了時に呼び出されます。

IDisposableこのクラス には 3 つの可能な実装があります。

  1. メンバーをDispose呼び出す最小限のメソッド - NO finalizerDispose()IDisposable
  2. IDisposableFinalizer を使用した標準的な実装ですが、通常の呼び出しがありません。GC.SuppressFinalize(this)Dispose()
  3. IDisposableFinalizer (およびGC.SuppressFinalize(this)call in ) を使用した完全な標準実装Dispose()

次の記述は正しいですか? 私はこれを正しく理解しましたか?

  1. ケース A. のオーバーヘッドは B. および C. よりもわずかに少なくなります。これは、オブジェクトにファイナライザーがないため、GC のファイナライズ キューに入らないためです。GC はコレクションの早い段階でこのオブジェクトを消去できるため、オーバーヘッドはありません。
  2. ケース B. オブジェクトにファイナライザーがあるため、最終的に GC のファイナライザー キューに入り、ファイナライザーが呼び出されます (抑制されていないため) - ファイナライザーは既に呼び出されているため何もしない dispose を呼び出します。これにより、オブジェクトがファイナライザー キューにあるというわずかなオーバーヘッドと、ファイナライザー呼び出しのごくわずかなオーバーヘッドが発生します。
  3. ケース C. オブジェクトにファイナライザーがあるため、GC のファイナライザー キューに入れられます。disposeSuppressFinalizeが呼び出されたため、ファイナライザーは実行されません。この場合でも、オブジェクトがファイナライザー キューに入るというわずかなオーバーヘッドが発生しますが、ファイナライザーは実際には実行されません。

ここでの重要な点は、「呼び出すことでファイナライザーのオーバーヘッドを回避した」と考えたくなるということですが、それは正しくない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);
}
4

3 に答える 3

3

.NET GC のバージョンが異なると動作が異なる場合がありますが、私の理解では、Finalizeメソッドを持つオブジェクトはすべて「ファイナライザー キュー」(放棄された場合に通知を要求したオブジェクトのリスト) に追加され、そのキューに長く留まります。それが存在するように。ファイナライズのために登録解除および再登録するメソッド (IMHO は の保護されたメンバーである必要があります) は、オブジェクトを「freachable queue」(メソッドが実行されるObjectオブジェクトのリスト) に移動する必要があるかどうかを制御するオブジェクトヘッダーのフラグを設定またはクリアしますfinalizeただし、オブジェクトがファイナライザ キューに追加または削除されることはありません。

したがって、オーバーライドするすべての型のすべてのインスタンスは、それが存在する限り、それが関与するすべてのガベージ コレクション サイクルにFinalize小さいがゼロではないオーバーヘッドを課します。オブジェクトを放棄する前に呼び出すと、オブジェクトが freachable キューに移動されるのを防ぐことができますが、オブジェクトが存在している間ずっとファイナライズ可能なキューにあったことによるオーバーヘッドはなくなりません。SuppressFinalize

私は、公開オブジェクトがFinalize. メソッドにはいくつかの正当な使用Finalize法がありますが、メソッドをオーバーライドするクラスは、ファイナライズに不要なものへの参照を保持しないようにする必要があります (ファイナライズ可能なオブジェクトが弱参照への唯一の参照を保持している場合、弱参照はファイナライザーが実行される前に無効になる可能性があります)。 、弱い参照のターゲットがまだ生きている場合でも)。

于 2015-03-19T16:40:44.967 に答える
1

アンマネージ リソースをクリーンアップする必要があるオブジェクトにのみ、ファイナライザーを含める必要があります。管理メンバーしかないので、ファイナライザーは必要ありません。メンバーにファイナライザーがあり、GC.SuppressFinalize()呼び出されない場合、ファイナライザーはメンバー自体で実行されます。

ファイナライザの目標は、GC が必要と判断したときにアンマネージド リソースとそのマネージド ラッパーDisposeをクリーンアップすることですが、パターンの目標は、特定の瞬間に任意のタイプのリソースをクリーンアップすることです。

「SuppressFinalize を呼び出してファイナライザーのオーバーヘッドを回避した」と考えるべきではありません。代わりに、「アンマネージ リソースをクリーンアップしたので、ファイナライザーを実行する必要はない」と考えるべきです。

番号付きの質問について:

  1. はい、ファイナライザーを実行すると、収集時にオーバーヘッドが発生します
  2. はい、Dispose()既に破棄されたオブジェクトの呼び出しは無視する必要があります
  3. これらのクラスは、インスタンスが作成されるときにファイナライズ キューに追加されますが、GC がそれを収集するように指示するときに freachable キューには追加されません。後で無視するためだけにオブジェクトをキューに入れるのは意味がありません。このリンクも参照してください。
于 2015-03-19T10:53:37.360 に答える