マルチスレッド Web サーバーとして機能する C# プログラムがあります。Sql Server データベースに保持されている Xml 構造の多くの処理を行います。
これらの Xml 構造のサイズが大きくなると、アプリのメモリが不足していることがわかります。
私は ANTS メモリ プロファイラーを展開して何が起こっているかを確認し、処理中にメモリに保持される大きな文字列の数を減らすことに成功し、状況を少し改善しました。
ただし、接続プールに保持されている大きなバイト配列が原因で、フラグメント化された大きなオブジェクト ヒープが残っています。大きなバイト配列は
TdsParserStateObject._bTmp
in TdsParser._physicalStateObj
in SqlInternalConnectionIds._parser
in DbConnectionInternal[0]
in DbConnectionPool._objectList
スレッドの実行中はスレッドごとに 1 つの接続を開いたままにしていますが、using ステートメント内でのみ接続を使用していると 99.9% 確信しています (これは最適化を目的としていますが、事態を悪化させているかどうかは疑わしいです)。
接続が保持するメモリの量を減らすために接続に対してできることはありますか (接続を閉じるか破棄する以外に)。
または、使用するたびにすべての接続をすぐに閉じるか破棄する必要がありますか?
[後で-私のコメントに従って]データベースアクセスごとに新しい接続を使用するようにコードをリファクタリングし、後で破棄します(もちろん、トランザクションの最初から最後まで同じ接続を使用するトランザクションを除き、取引で処分します。)
プログラムがアイドル状態 (つまり、接続が使用されていない状態) であっても、接続プールには大量のメモリを占有する接続が存在し、断片化を引き起こします。
破棄された接続が接続プールに 58MB のメモリを保持するのはなぜですか?
[さらに後で] Sql Server 接続プールが大きなヒープを断片化するのを防ぐ解決策があります。これは、おそらく巨大なバッファーを持つ接続を検出し、破棄時にプールから削除するようにマークすることです。
SqlConnection.ClearPool(接続)
私は現在、DataReader をサブクラス化し、返されたフィールドのサイズが 10MB を超えているかどうかを検出するという、かなりハックな方法でこれを行っています。
どの接続に大きなバッファがあるかを検出するより良い方法についての提案を歓迎します。
トランザクション内でどの接続を使用するかを追跡する (明らかにトランザクションである必要があります) ため、すべてのデータベース アクセスで接続を開いて破棄するように変更を元に戻したことに注意してください。
スレッドの開始時に開かれ、最後に閉じられた接続は問題ありません。ほぼすべてのスレッドが存続期間が短い (単一の Web 要求への応答であるため) ためです。例外は、とにかく単一のトランザクションのコンテキストでおそらく動作するバッチ プロセスです。