17

WCF Web サービスに問題があり (ダンプ、メモリ リークなど)、プロフィル ツール (ANTS メモリ プロファイル) を実行しました。

処理が終了しても (特定のテストを実行してから停止しました)、ジェネレーション 2 は Web サービスのメモリの 25% であることがわかりました。このメモリを追跡して、ハッシュ コードが -1 の (null、null) アイテムでいっぱいの辞書オブジェクトがあることを発見しました。

Web サービスのワークフローは、特定の処理中に項目が追加され、辞書から削除されることを意味します (単純なAddandのみRemove)。大したことではありません。しかし、すべての項目が削除された後、辞書は (null, null) でいっぱいになっているようですKeyValuePair。実際、何千ものそれらがメモリの大部分を占有し、最終的にはオーバーフローが発生し、対応する強制アプリケーション プール リサイクルと DW20.exe が取得できるすべての CPU サイクルを取得します。

ディクショナリは実際にはDictionary<SomeKeyType, IEnumerable<KeyValuePair<SomeOtherKeyType, SomeCustomType>>>( System.OutOfMemoryException が原因で Large Dictionary ) であるため、何かを保持する何らかの参照があるかどうかを既に確認しました。

ディクショナリは静的オブジェクトに含まれているため (処理を通じてさまざまな処理スレッドにアクセスできるようにするため)、この質問やその他の多くの質問 (静的メンバーはガベージ コレクションを取得しますか? ) から、そのディクショナリが第 2 世代にある理由がわかりました。しかし、これはそれらの原因も(null、null)?辞書からアイテムを削除しても、メモリ内で常に何かが占有されますか?

この質問のような速度の問題ではありませんC# の大きなデータ構造からメモリを解放します。メモリが再利用されることはないようです。

(null、null) ペアで埋め続けるだけでなく、実際に辞書からアイテムを削除するためにできることはありますか? 他にチェックアウトする必要があるものはありますか?

4

3 に答える 3

16

辞書はアイテムをハッシュテーブルに保存します。これには、配列が内部的に使用されます。ハッシュテーブルの動作方法により、この配列は常に実際に格納されているアイテムの数よりも大きくなければなりません(少なくとも約30%大きくなります)。Microsoftは72%の負荷率を使用します。つまり、配列の少なくとも28%が空になります( C#2.0 、特にSystem.Collections.HashtableクラスSystem.Collections.Generic.Dictionaryクラスを使用したデータ構造の広範な調査を参照してください)。 )したがって、null/nullエントリはこの空き領域を表すことができます。

配列が小さすぎると、自動的に大きくなります。ただし、アイテムが削除されても配列は縮小されませんが、解放されるスペースは、新しいアイテムが挿入されたときに再利用する必要があります。

この辞書を管理している場合は、辞書を縮小するために再作成を試みることができます。

theDict = new Dictionary<TKey, IEnumerable<KeyValuePair<TKey2, TVal>>>(theDict);

ただし、問題は実際の(空ではない)エントリから発生する可能性があります。ディクショナリは静的であるため、別のディクショナリまたはnulltheDict = new ...またはtheDict = null)を割り当てない限り、ガベージコレクタによって自動的に再利用されることはありません。これは、静的な辞書自体にのみ当てはまり、そのエントリには当てはまりません。削除されたエントリへの参照が別の場所に存在する限り、それらは存続します。GCは、何らかの参照によってアクセスできなくなったオブジェクト(以前または後で)を再利用します。このオブジェクトが静的であると宣言されているかどうかに関係なく、違いはありません。オブジェクト自体は静的ではなく、それらの参照のみです。


@RobertTausigが親切に指摘したように、.NET Core 2.1以降Dictionary.TrimExcess()、実際に必要だった新しいがありますが、当時は存在していませんでした。

于 2013-02-22T12:26:12.393 に答える
5

そのdictのスペースを定期的にリサイクルする必要があるようです。新しいものを作成することでそれを行うことができます:new Dictionary<a,b>(oldDict)。必ずスレッドセーフな方法でこれを行ってください。

いつこれを行うのですか?タイマーのティック(60秒?)または特定の書き込み回数(100k?)が発生したとき(変更カウンターを保持する必要があります)。

于 2013-02-22T12:08:23.397 に答える
0

解決策は、静的辞書でClear () メソッドを呼び出すことです。このようにして、ディクショナリへの参照は引き続き使用できますが、含まれているオブジェクトは解放されます。

于 2013-08-27T10:23:25.670 に答える