3

ガベージ コレクションのテストを作成していて、デバッグ モードで発生する一連の奇妙なバグに遭遇しました。これは、抽出された POC コードです。20 個のラムダのリストと、それらへの弱参照のリストがあります。13 番目のラムダにアクセスし (.ToString() を呼び出して)、リストをクリアします。次に、ガベージ コレクションを強制し、クリーンアップで残った要素を分析します。

    public void TestGC_WTF() {
        var handlers = new List<Action>();
        var weakReferences = new List<WeakReference>();

        int testValue = 0;
        for (int i = 0; i < 20; i++) {
            int number = i;
            Action handler = () => testValue += number;
            handlers.Add(handler);
            weakReferences.Add(new WeakReference(handler));
            handler = null;
        }

        handlers[13].ToString();

        handlers.Clear();

        GC.Collect();

        if (false) { } //This is required for the bug to occur.

        var aliveReferences = Enumerable.Range(0, weakReferences.Count).Where(i => weakReferences[i].IsAlive).ToArray();
        Console.WriteLine("Uncollected handlers: {0}", string.Join(",", aliveReferences));
    }

デバッグ モードでは、このコードは ("Uncollected handlers: 13,19") を出力します。この結果で発生する問題は次のとおりです。

  1. 19 番目の要素は収集されません (各ループ反復の最後にハンドル変数を明示的に null に設定しても)
  2. 13番目の要素は収集されません(どこにも保存していませんが)
  3. if (false) { }(またはその他のループ/条件) は、バグをトリガーするために不可欠です。

問題の原因は何ですか?(デバッグでは、オブジェクトが定義されているブロックの最後まで保持されることを知っています。これだけでは、私が抱えている問題はまだ説明されていません。)

Microsoft にバグを提出しましたが、問題の原因にもっと興味がありますhttps://connect.microsoft.com/VisualStudio/feedback/details/775082/strangely-triggered-strange-bugs-with-garbage -デバッグビルドのコレクション

4

2 に答える 2

3

これは、x86 ジッターに固有のものです。はい、これはバグのようです。ジッターは、スタック フレームの一時的な保存場所を有効なオブジェクト参照としてマークしています。[ebp-74h] と同様に、handler[13] への参照が格納されるスロットと、handler[19] への参照を保持する [ebp-68h] です。これらの一時変数は、x86 コードでは一般的です。これは、CPU レジスタの数が少なく、オプティマイザが無効になっているときに、ジッタが少数のレジスタを有効に使用するために費やす労力が少ないためです。

connect.microsoft.com でフィードバック レポートを提出できます。ただし、修正される可能性は非常に低く、オプティマイザーが無効になっている場合、ジッターはこれを機能させる義務はありません。デバッグを容易にするために、ローカル変数の有効期間はメソッドの最後まで延長されます。宣言されたローカル変数だけでなく、ジッターが割り当てる一時的な変数にも当てはまります。そしてもちろん、最終的にはほとんど問題になりません。リリース ビルドを顧客に出荷するだけです。最適化されたリリース ビルドが散らばっている場合は、お気軽にこれを取り上げてください。これは GC の有効性に悪影響を及ぼします。

于 2012-12-24T18:58:36.307 に答える
2

考えられるすべてのガベージを必ずしも収集するとは思わずGC.Collect()、そうするために労力を費やすだけです。デバッグ ビルドでは、デバッガーを使用してそれらを検査する可能性が高いため、最適化では参照を有効に保つことが優先されます。ここでは、バグと呼ばれる動作は見られません。

于 2012-12-24T16:41:48.543 に答える