1

c++dllとの通信に使用されるアンマネージメモリ構造がいくつかあります。このような各構造は手動で解放する必要があるため、MyUnmanagedStructureを実装するaでラップしますIDisposable

私は常にこれらの構造の可変数を一緒に必要とするので、MyUnmanagedStructureCollectionIDisposableも実装するコレクションがあります。

(最小限のサンプルコードについては、以下を参照してください)

私のライブラリのユーザーが常にDispose()を呼び出すか、コレクションをラップする限りusing() {}問題はありませんが、それを保証することはできません。ユーザーがコレクションを手動で破棄しなくても、メモリをリークしたくありません。

代わりにファイナライザーを介してガベージコレクションによってメソッドが呼び出された場合、MyUnmanagedStructureCollection.Dispose()私が理解している限り、自分private List<MyUnmanagedStructure>がまだガベージコレクションされていないことを確認できません。その場合、各構造をどのように破棄できますか?

ファイナライズコードで、リストがまだガベージコレクションされていないことを期待して、リストを反復処理する必要がありますか?

これをtry/catchブロックで実行して、ObjectDisposedExceptionをキャッチすることをお勧めしますか?

または、個々のファイナライザーに依存して、各unmanagedStructureを「それ自体を守る」ようにし、コレクションのファイナライザーで何もしないようにする必要がありますか?

public class MyUnmanagedStructureCollection : IDisposable
{
    private List<MyUnmanagedStructure> structures;
    private bool disposed = false;

    #region standard IDIsposable pattern
    public ~MyUnmanagedStructureCollection()
    {
        this.Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            // Dispose unmanaged resources 
            // Should not access managed resources, 
            // the garbage collection may have claimed them already!

            // PROBLEM!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            // this.structures is a List<MyUnmanagedStructure>; so is a managed resource!!!

            foreach (var structure in this.structures)
                 structure.Dispose(disposing)
            this.removeAllMemoryPressure();

            if (disposing) 
            {
                // Dispose managed resources.
                this.structures.Clear();
                this.structures = null;
            }

        }
        disposed = true;
    }
}


public class MyUnmanagedBuffer : IDisposable
{
...
}
4

2 に答える 2

2

GC の仕組みは次のとおりです。

  1. 到達可能なすべてのオブジェクトを見つける
  2. ファイナライザを持つすべての到達不能オブジェクトをファイナライズ キューにエンキューする
  3. ファイナライズ キューから到達可能なすべてのオブジェクトを到達可能としてマークする
  4. 残りの到達不能オブジェクトを解放する

ファイナライザーが実行されているオブジェクトから参照されるオブジェクトは、まだガベージ コレクションされていてはなりません。

注意が必要なのは、ファイナライズの順序が未定義であることだけです。そのため、リストの要素はまだ確定されていても、収集されていない可能性があります。ファイナライズはシングル スレッドであることが保証されているため、ロックも必要です。

独立したファイナライズはより単純であるため、通常、このようなファイナライズ チェーンを回避しようとします。しかし、あるオブジェクトを他のオブジェクトの前に破棄する必要がある場合、そのような構造は避けられません。

sを使用した重要なファイナライズも検討する必要がありますSafeHandle


到達可能性

ファイナライズのガイドラインの 1 つは、Finalize メソッドが他のオブジェクトに触れないようにすることです。これは、他のオブジェクトがすでに収集されているためであると誤解されることがあります。それでも、説明したように、ファイナライズ可能なオブジェクトから到達可能なグラフ全体がプロモートされます。

ガイドラインの本当の理由は、既にファイナライズされている可能性のあるオブジェクトに触れないようにすることです。これは、ファイナライズが順序付けられていないためです。

ファイナライズに関する Chris Brumme

于 2012-08-27T13:11:19.167 に答える
1

あなたMyUnmanagedBufferのクラスはクラスの観点からは管理されたリソースMyUnmanagedStructureCollectionなので、そのように処理する必要があると思います。これは、MyUnmanagedStructureCollection.Dispose(bool)メソッドが以下のようになることを意味します。

protected virtual void Dispose(bool disposing) {
    if (!disposed) {
        // Dispose unmanaged resources 
        // Should not access managed resources, 
        // the garbage collection may have claimed them already!

        if (disposing) {
            // Dispose managed resources.
            // This means that we try to dispose all items in the structures collection.
            if (this.structures != null) {
                foreach (var structure in this.structures) {
                    structure.Dispose(disposing);
                    this.removeAllMemoryPressure(); // What does this?
                }
                this.structures.Clear();
                this.structures = null;
            }
        }
    }

    disposed = true;
}
于 2012-08-27T13:25:12.233 に答える