MSMQ非同期IOを使用するときの.NETヒープの断片化
-
12-12-2019 - |
質問
私は大量のMSMQキュー(現時点で約10000程度)から読み取るアプリケーションを持っています。
uint32.maxValueタイムアウトでqueue.BeginPeek
を使用して、キューからメッセージを受信します。メッセージがキューに表示されたら、それを処理してqueue.BeginPeek
をもう一度呼び出します。だから私はすべてのキューを聴きますが、メッセージ処理はスレッドプールで行われます。
私は、記憶の使用量がゆっくり成長することに気づいた(2週間の仕事は200 MBから800 MBの成長を引き起こす)。ダンプファイルを調べた後、私は多くの空きオブジェクトを持つ典型的なヒープフラグメンテーション画像を参照してください(それらのいくつかは数メガバイトのサイズを持っています)。そして穴の間に固定されたオブジェクトがあります。
これは、管理されていないコードへの呼び出しを操作するときに共通の状況であると思われます。しかし、私はインターネット内で解決策を見つけませんでした。
は、.NETではメモリ管理も純粋で、そのような簡単なシナリオでさえ完了しない、または何かが恋しいですか?
編集:私はサンプルアプリケーションで調査しました。固定オブジェクト間の穴(フリーメモリゾーン、いわゆる空きオブジェクト)は、メモリを新しいオブジェクトに割り当てるときにGCによって再利用されます。 しかし、私の生産アプリケーションでは、固定されたオブジェクトは長い生きています、そして彼らは彼らの間の穴を持つ第2世代についに現れます(GCはそれを分離する境界線をシフトするだけです)。私は本当に普通の長い生きているものをほとんど持っていないので、私はダンプファイルの2世代の2世代の穴を見ます。
だから私のアプリケーションのメモリ消費は10000 *(平均穴の大きさ)に成長することができます。 (10000は将来増加する可能性があるキューの数です)。現時点でこれを修正する方法はありません。唯一の方法は時々アプリケーションを再起動することです。
もう一度尋ねることができます。なぜ.NETに固定オブジェクトに別々のヒープがないのですか? (たぶんこれは初心者の質問です)。現時点で、管理されていないコードを扱う非同期操作を呼び出すことが、メモリの問題を引き起こす可能性があることがわかりました。
解決
MSMQの管理対象ラッパーのソースコードを見た後、APIを使用して本物の問題につまずくようです。BeginPeek
を呼び出すと、管理されていないAPIに渡される前にピン止めされたプロパティのコレクションが作成されます。メッセージが受信された場合のみ、これらのプロパティはunpinnedではなく、この時点でBeginPeek
を呼び出す必要があるメッセージを受信し続けることができます。
この断片化が本当の関心事である場合は、HACK唯一のハックが毎時存在すること、またはそれ以外の呼び出しは、BeginPeek
へのすべての通話をキャンセルし、ガベージコレクションを強制してから通常のリスニング操作を再開する必要があります。これにより、ガベージコレクタがフラグメント化を処理できるようにする必要があります。
他のヒント
welyは、長寿命の固定オブジェクトの問題である場合、単純な作業範囲は、短い期間でBeginPeek
でタイムアウトを使用して次世代に移動し続けることです。各タイムアウトとEndPeek
の後、BeginPeek
を再発行することができます。MARTINが参照および廃棄されたプロパティがいつ実行されたかに応じて、キューオブジェクト(キュー自体ではなく、amangedラッパーだけではありません)を再作成する必要があります。運をめちゃくちゃにするのは、それを遠くに持っていく必要はありません。