3

私の理解では、GC がメイン グラフから (強い参照を介して) アクセスできなくなったオブジェクトのサブグラフを見つけると、それらを収集してメモリを再利用します。私の質問は、アクセスできないオブジェクトが削除される順序に関するものです。これはアトミック操作として発生しますか? アクセスできないオブジェクトはすべて一度にファイナライズされますか?それとも、GC はアプリケーションの実行中に各オブジェクトを 1 つずつファイナライズしますか? オブジェクトが 1 つずつファイナライズされる場合、従う特定の順序はありますか?

オブジェクト B への弱い参照を保持するオブジェクト A がある場合、B のインスタンス メソッドを呼び出す前に、A が B がまだ生きているかどうかを確認する必要があることは明らかです。ここで、B が別のオブジェクト C への強い参照を保持しているとします。B がまだ生きている場合、C もまだ生きていると常に保証されますか? GC がコレクションのために B と C の両方をマークした可能性はありますが、C は B の前にファイナライズされますか?

私の推測では、B から C にアクセスすることは常に安全であると思います (これは強力なリファレンスであるため)。しかし、私が間違っていると、非常に断続的な追跡困難なバグが発生する可能性があるため、この仮定を確認したいと思います。

public class ClassA
{
    private readonly WeakReference objBWeakRef;

    public ClassA(ClassB objB)
    {
        objBWeakRef = new WeakReference(objB);
    }

    public void DoSomething()
    {
        // The null check is required because objB 
        // may have been cleaned-up by the GC
        var objBStrongRef = (ClassB) objBWeakRef.Target;
        if (objBStrongRef != null)
        {
            objBStrongRef.DoSomething();
        }
    }
}

public class ClassB
{
    private readonly ClassC objCStrongRef;

    public ClassB(ClassC objC)
    {
        objCStrongRef = objC;
    }

    public void DoSomething()
    {
        // Do I also need to do some kind of checking here?
        // Is it possible that objC has been collected, but 
        // the GC has not yet gotten around to collecting objB?
        objCStrongRef.DoSomething();
    }
}

public class ClassC
{
    public void DoSomething()
    {
        // do something here... if object is still alive.
    }
}
4

4 に答える 4

4

はい、を介してインスタンスへClassBの参照がある場合、がまだ生きていれば、ランダムに蒸発することを心配する必要はありません。これに対する例外は、ファイナライザーを作成する場合です。ClassCobjCStrongRefClassBthe ClassC

~ClassB() { ...}

そこでは、話しかけようとしてはいけませobjCStrongRef 。どのオブジェクトが最初にファイナライズされるか見当がつかないからです。ClassCファイナライズで何かが必要な場合は、別のものを~ClassC()用意する必要があります。ただし、実際にはファイナライザー メソッドは非常にまれであり、正当な理由 (管理されていないハンドルなど) がなければ追加しないでください。

完全なループが可能であり、任意の場所で中断する必要があるため、特定の順序に従う必要はありません。

于 2012-11-20T07:29:37.450 に答える
2

通常のマネージ メソッドでは、B が生きている限り、B が強い参照で参照するすべてのものが生きていることに依存できます。B から C への参照は、B の存続期間中有効です。

例外はファイナライズ コードにあります。ファイナライザーを実装する場合 (通常ではなく、異常なケースと見なす必要があります)、すべての賭けはファイナライザーの呼び出しチェーン内でオフになります。オブジェクトがライブ オブジェクトによって参照されなくなったことを GC が認識した後、ファイナライザーは長時間実行されない場合があります。極度のパニック シャットダウン コーナー ケースでは、ファイナライザーがまったく実行されない場合があります。ファイナライザーが実行されるスレッドや、ファイナライザーが実行される順序については何も想定できません。また、オブジェクト内の参照変数を参照することは避ける必要があります。それらが既にファイナライズされているかどうかがわからないからです。

ただし、これはすべてアカデミックです。コード例はファイナライザーを実装していないため、bizzarro ファイナライザーの世界について心配する必要はありません。

于 2012-11-20T07:29:23.273 に答える
0

オブジェクトに到達できるあらゆる種類の参照パスが存在しない場合、GC が実行されてそのメモリ空間が再利用可能になり、そのメモリ空間が使用可能になるまで、オブジェクトが占有していたメモリ空間を調べる手段はありません。それを使用する新しいオブジェクトが作成されます。その時までに、古いオブジェクトはもう存在しません。GC の実行時期に関係なく、オブジェクトへの最後の参照が破棄されるかアクセス不能になると、オブジェクトは即座にアクセス不能になります。

「ファイナライズキュー」と呼ばれるデータ構造がそのようなすべてのオブジェクトへの参照を保持するため、アクティブなファイナライザーを持つオブジェクトには常に参照パスがあります。このキュー内のオブジェクトは、GC によって最後に処理されたものです。オブジェクトがキューによって参照されていることが判明した場合、参照は「freachable」キューと呼ばれる構造に格納されます。このキューにはFinalize、最初の機会にメソッドを実行する必要があるオブジェクトがリストされます。GC サイクルが完了すると、このリストはルート化された強力な参照と見なされますが、ファイナライザー スレッドはそこから何かを引き出し始めます。通常、項目がリストから取り出されると、それらへの参照はどこにも存在せず、消えてしまいます。

オブジェクトへの弱い参照が存在する場合でも、オブジェクトはコレクションの対象と見なされるため、弱い参照は別の問題を追加します。それらが機能していると見なす最も簡単な方法は、保持が必要なすべてのオブジェクトが識別されると、システムはWeakReference保持を必要としないすべてのターゲットを調べて無効にすることです。WeakReferenceそのようなインスタンスがすべて無効化されると、オブジェクトにアクセスできなくなります。

アトミック セマンティクスが重要になる唯一の状況は、2 つ以上のWeakReferenceインスタンスが同じオブジェクトを対象とする場合です。そのシナリオでは、理論的には、GC が同じターゲットを持つ別のスレッドを無効にしているときに、あるスレッドが のTargetプロパティにアクセスする可能性があります。このような状況が実際に発生する可能性はないと思います。同じターゲットを共有する複数のインスタンスがそれぞれの も共有することで防ぐことができます。その場合、ターゲットへのアクセスがすぐに発生してオブジェクトを存続させるか、ハンドルが無効になり、ハンドルへの参照を保持するすべてのインスタンスが事実上無効になります。WeakReferenceWeakReferenceWeakReferenceGCHandleWeakReference

于 2013-01-29T23:13:52.103 に答える
0

Jeffrey Richter による「C# 経由の CLR」から:

さらに、Finalize メソッドが実行されるタイミングを制御できないことに注意してください。Finalize メソッドは、ガベージ コレクションが発生したときに実行されます。ガベージ コレクションは、アプリケーションがより多くのメモリを要求したときに発生する可能性があります。また、CLR は Finalize メソッドが呼び出される順序を保証しないため、Finalize メソッドを定義する型を持つ他のオブジェクトにアクセスする Finalize メソッドを記述しないようにする必要があります。それらの他のオブジェクトはすでにファイナライズされている可能性があります。ただし、Finalize メソッドを定義していない値型インスタンスまたは参照型オブジェクトにアクセスすることはまったく問題ありません。静的メソッドを呼び出すときも注意が必要です。これらのメソッドは、ファイナライズされたオブジェクトに内部的にアクセスする可能性があり、静的メソッドの動作が予測不能になる可能性があるためです。

于 2012-11-20T07:29:12.610 に答える