4

C ++ / CLI DLLでクラスを実装する場合:

public ref class DummyClass
{
protected:
    !DummyClass() 
    {
        // some dummy code:
        std::cout << "hello" << std::endl;
    }
}

そのDLLをC#プロジェクトにロードし、オブジェクトを繰り返し作成するだけでクラスを使用すると、次のようになります。

static void Main()
{
    while (true)
    {
        var obj = new DummyClass();
    }
}

次に、プログラムの実行中に、メモリはゆっくりとOutOfMemoryExceptionにダイジェストされます。

このメモリリーク(またはガベージコレクションの悪い作業)は、C ++/CLIでファイナライザーを実装するたびに発生するようです。

なぜこのメモリリークが発生するのですか?どうすればそれを回避し、ファイナライザーを他の(より複雑な)用途に使用できるでしょうか?


更新:原因は確かにコンソール/ stdoutまたはファイナライザーの他の非標準コードへの書き込みではありません。次のクラスは同じメモリリーク動作をします:

public ref class DummyClass
{
private:
    double * ptr;
public:
    DummyClass()
    {
         ptr = new double[5];
    }
protected:
    !DummyClass() 
    {
         delete [] ptr;
    }
}
4

2 に答える 2

4

ガベージコレクションよりも速く割り当てると、OOMに遭遇します。大量の割り当てを行う場合、CLRは割り当てを調整するためにSleep(xx)を挿入しますが、極端な場合はこれでは不十分です。

ファイナライザーを実装すると、オブジェクトはファイナライズキューに追加され、ファイナライズされるとキューから削除されます。これにより、追加のオーバーヘッドが発生し、オブジェクトの寿命が必要以上に長くなります。安価なGen0GC中にオブジェクトを解放できたとしても、ファイナライズキューによって参照されます。フルGCが発生すると、CLRはファイナライズスレッドをトリガーしてクリーンアップを開始します。ファイナライズできるよりも速く割り当てを行うため(stdoutへの書き込みは非常に遅い)、ファイナライズキューがどんどん大きくなり、ファイナライズ時間がますます遅くなるため、これは役に立ちません。

私はそれを測定していませんが、オブジェクトの寿命が長くなり、2つのファイナライズキューの処理(ファイナライザーキューとf-reachableキュー)が割り当てよりもファイナライズを遅くするのに十分なオーバーヘッドを課すため、空のファイナライザーでもこの問題が発生すると思います。

ファイナライズは固有の非同期操作であり、特定の時点での実行が保証されていないことを覚えておく必要があります。CLRは、追加の割り当てを許可する前に、保留中のすべてのファイナライザーをクリーンアップするのを待つことはありません。10スレッドに割り当てる場合でも、ファイナライザスレッドは1つクリーンアップされます。決定論的なファイナライズに依存したい場合は、GC.WaitForPendingFinalizers()を呼び出して待機する必要がありますが、これによりパフォーマンスが大幅に停止します。

したがって、あなたのOOMが期待されます。

于 2013-03-20T12:02:26.937 に答える
0

AddMemoryPressure関数を使用する必要があります。そうしないと、ガベージコレクターがこれらのオブジェクトをタイムリーにクリーンアップする必要性を過小評価します。

于 2013-03-21T07:14:09.250 に答える