これは(必然的に)メモリリークですか?
はい、いいえ。ほとんどの場合、C# アプリで真のメモリ リークが発生することはありません。内部でネイティブ リソースを使用する型でさえ、Dispose() が呼び出されなかった場合、ファイナライザーでクリーンアップされます。
いずれにせよ、クリーンアップに時間がかかりすぎる古い参照やネイティブ リソース (または自分自身をリークする適切に記述されていないネイティブ関数) が存在する可能性があります。これは、アンマネージ言語で発生する真のメモリ リークと同じ実際的な結果をもたらします。
そして、強制GCが役に立たないのはなぜですか?
あなたは GC に提案をしましたが、GC はそれを採用しませんでした。GC.Collect()
GC がすべてをクリーンアップすることを強制するわけではなく、ドキュメンテーションがそれを教えてくれます。第二に、このメモリ使用量は実際に問題を引き起こしていますか? どのくらいのメモリが消費されていますか? 解放されることはありますか?少量であれば、おそらく何も心配する必要はありません。GC に任せてください。
あなたのコードを見なくても、私にできることは推測することだけです。C# アプリでメモリ不足が発生する原因はいくつかあります。
古い参照。決して消去しない「死んだ」オブジェクトへの参照のリスト。イベント ハンドラーもこれを引き起こす可能性があります。たとえば、イベント デリゲートはサブスクライバーへの参照を保持しているため、静的イベントは常にサブスクライブ解除する必要があります。
そのため、サブスクライバーがイベントのサブスクライブを解除せずにスコープ外に出ると、これらの「死んだ」オブジェクトへの有効な参照が常に存在するため、本質的にメモリ リークが発生します。親 (イベント パブリッシャー) は通常、必要なときにスコープ外になるため、イベントの一般的なユース ケースでは、これが発生しないことに注意してください。
IDisposable を実装するオブジェクトを破棄しない。確かに、それらはおそらくファイナライザーで自分自身をクリーンアップします (すべての .NET IDisposable 型が行い、残りの型も行う必要があります) が、ファイナライザーは不確定なタイミングで実行されるため、全体が予測不能になります。Dispose() (using
可能な場合はステートメントを使用) をできるだけ早く呼び出すようにしてください。
大きなオブジェクト ヒープの断片化。大量の「大きな」オブジェクト (85,000 バイトを超えるオブジェクト) を割り当てている場合、このメモリ セグメントが断片化される可能性があります。このメモリは、標準ヒープに割り当てられた第 0 世代および第 1 世代のオブジェクトほど頻繁にはクリーンアップされません。
.NET GC は複雑であり、通常は非常にうまく機能します。を呼び出すことが許容される場合もありGC.Collect
ますが、いつそれが良い考えなのかを理解する必要があります。たとえば、存続期間の短いオブジェクトを大量に作成する場合です。
他の手段を使い果たしたときに、RedGate の .NET メモリ プロファイラーを使用して良い経験をしました。ご存知のように、私は彼らのために働いていませんし、私は彼らと何らかの形で提携していません. 一見の価値あり。