マイクロソフトのドキュメント](https://docs.microsoft.com/dotnet/api/system.idisposable)を読むと、`IDisposable`インターフェイスの "primary"用途は、管理されていないリソースをクリーンアップすることだとわかります。
私にとって、quot;unmanaged"とは、データベース接続、ソケット、ウィンドウハンドルなどのことです。 しかし、
Dispose()`メソッドが_管理されたリソースを解放するために実装されているコードを見たことがありますが、ガベージコレクタがそれを処理すべきなので、私には冗長に思えます。
例えば、以下のようなものです。
public class MyCollection : IDisposable
{
private List<String> _theList = new List<String>();
private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();
// Die, clear it up! (free unmanaged resources)
public void Dispose()
{
_theList.clear();
_theDict.clear();
_theList = null;
_theDict = null;
}
質問ですが、これによってガベージコレクタは MyCollection
が使用するメモリを通常よりも速く解放することができますか?
編集。 これまでに、IDisposableを使ってデータベース接続やビットマップなどの管理されていないリソースをクリーンアップする良い例が投稿されています。 しかし、上のコードの _theList
に100万個の文字列が含まれていて、ガベージコレクタを待つのではなく、今すぐにでもそのメモリを解放したいとします。 上記のコードでそれが可能でしょうか?
IDisposableは、
using`文を利用して、マネージドオブジェクトを決定論的にクリーンアップするための簡単な方法を利用するためによく使われます。
public class LoggingContext : IDisposable {
public Finicky(string name) {
Log.Write("Entering Log Context {0}", name);
Log.Indent();
}
public void Dispose() {
Log.Outdent();
}
public static void Main() {
Log.Write("Some initial stuff.");
try {
using(new LoggingContext()) {
Log.Write("Some stuff inside the context.");
throw new Exception();
}
} catch {
Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
} finally {
Log.Write("Some final stuff.");
}
}
}
特に、.Clear()
の呼び出しがそうです。
あなたの編集に対する答え:ちょっとね。 これをやると
public void WasteMemory()
{
var instance = new MyCollection(); // this one has no Dispose() method
instance.FillItWithAMillionStrings();
}
// 1 million strings are in memory, but marked for reclamation by the GC
メモリ管理の目的上、これと機能的には同じです。
public void WasteMemory()
{
var instance = new MyCollection(); // this one has your Dispose()
instance.FillItWithAMillionStrings();
instance.Dispose();
}
// 1 million strings are in memory, but marked for reclamation by the GC
どうしても今すぐにメモリを解放する必要がある場合は、GC.Collect()
を呼び出してください。 しかし、ここでこれを行う理由はありません。 メモリは必要になったときに解放されます。
MyCollection`がどのみちガベージコレクションされるのであれば、それを処分する必要はありません。 そうすると、必要以上にCPUを酷使することになりますし、ガベージコレクタがすでに実行した事前計算された分析が無効になることさえあります。
私はIDisposable
を使って、管理されていないリソースと一緒に、スレッドが正しく廃棄されるようにしています。
EDIT Scottさんのコメントを受けて。
GCのパフォーマンス指標が影響を受けるのは、GC.Collect()が呼び出されたときだけです」_。
概念的には、GCはオブジェクト参照グラフのビューと、スレッドのスタックフレームからのすべての参照を維持します。 このヒープは非常に大きく、メモリの多くのページに及ぶことがあります。 最適化のために、GC は、頻繁に変更されそうにないページの分析をキャッシュして、不必要にページを再スキャンしないようにしています。 GCは、ページ内のデータが変更されたときにカーネルから通知を受け取るので、そのページが汚れていて再スキャンが必要であることがわかります。 コレクションがGen0であれば、ページ内の他のものも変更されている可能性がありますが、Gen1やGen2ではその可能性は低くなります。 逸話によると、Mac OS XでSilverlightプラグインを動作させるためにGCをMacに移植したチームでは、これらのフックはMac OS Xでは利用できませんでした。
不必要な資源の浪費に対するもう一つのポイントは、プロセスがアンロードされる状況を想像してみてください。 プロセスがアンロードされている状況を想像してみてください。また、そのプロセスがしばらくの間実行されていたとします。おそらく、そのプロセスのメモリページの多くはディスクにスワップされているでしょう。 少なくとも、L1やL2のキャッシュには入っていないでしょう。 このような状況では、アンロード中のアプリケーションが、プロセスの終了時にOSが解放する予定のリソースを「解放」するために、データやコードページをすべてメモリにスワップする意味がありません。 これは、管理されているリソースにも、管理されていないリソースにも当てはまります。 バックグラウンドではないスレッドを存続させるためのリソースだけが廃棄されなければならず、そうでなければプロセスは存続してしまいます。
さて、通常の実行中には、管理されていないメモリリークを避けるために正しくクリーンアップされなければならない刹那的なリソースがあります(@fezmonkeyさんが指摘するように、データベース接続、ソケット、ウィンドウハンドル)。 これらは廃棄されなければならない種類のものです。 スレッドを所有するクラスを作成した場合(所有するというのは、スレッドを作成したという意味で、少なくとも私のコーディングスタイルでは、スレッドを確実に停止させる責任があります)、そのクラスはほとんどの場合、IDisposable
を実装し、Dispose
の間にスレッドを破棄しなければなりません。
.NETフレームワークでは、IDisposable
インターフェイスを、このクラスを廃棄しなければならないという開発者へのシグナル、あるいは警告として使用しています。 フレームワーク内で IDisposable
を実装している型(明示的なインターフェイスの実装を除く)で、廃棄がオプションになっているものは思いつきません。