C# で手動でクリーンアップする必要があるリソースはありますか?
-
02-07-2019 - |
質問
どのリソースを手動でクリーンアップする必要があるか C# そうしないとどのような結果が生じるのでしょうか?
たとえば、次のコードがあるとします。
myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black);
// Use Brush
destroy メソッドを使用してブラシをクリーンアップしない場合、ガベージ コレクターがプログラムの終了時に使用されたメモリを解放すると想定していますか?これは正しいです?
他に手動でクリーンアップする必要があるリソースは何ですか?
解決
技術的には、IDisposable から継承したものはすべて積極的に破棄する必要があります。'using' ステートメントを使用すると、作業を簡単にすることができます。
http://msdn.microsoft.com/en-us/library/yh598w02.aspx
ドキュメントのサンプル コードやツールによって生成されたコード (つまり、ビジュアルスタジオ)。
IDisposable の優れている点は、次のことができることです。 積極的に 基盤となるアンマネージド リソースを解放します。場合によっては、これを本当に実行したいことがあります。たとえば、ネットワーク接続やファイル リソースを考えてみてください。
他のヒント
何かを破棄しない場合、コード内にそれへの参照がなくなったことをガベージ コレクターが認識したときに、しばらくしてからクリーンアップされます。そのようなものについてはあまり問題になりませんが、開いているファイルの場合はおそらく問題になります。
一般に、何かに Dispose メソッドがある場合は、それを使い終わったときにそれを呼び出すか、可能であれば、それを using
声明:
using (SolidBrush myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black))
{
// use myBrush
}
- 内部 Windows データ構造へのハンドル。
- データベース接続。
- ファイルハンドル。
- ネットワーク接続。
- COM/OLE 参照。
リストは続きます。
電話することが重要です Dispose
あるいはさらに良いことに、 using
パターン。
using (SolidBrush myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black))
{
// use myBrush
}
何かを破棄しなかった場合、ガベージ コレクターがそれへの参照がなくなったことに気づいたときにクリーンアップされます。それはしばらくしてからになる可能性があります。
の場合 System.Drawing.Brush
, Windows は、すべてのプログラムがハンドルを解放するまで、メモリにロードされたブラシの内部 Windows 構造を保持します。
IDisposables を破棄しない場合の影響は、ごくわずかなパフォーマンスの低下からアプリのクラッシュまでさまざまです。
あなたの例の Brush オブジェクトは、必要に応じて GC によってクリーンアップされます。しかし、プログラムは、以前にクリーンアップすることで獲得できたはずの余分なメモリの恩恵を享受できません。多くの Brush オブジェクトを使用している場合、これは重要になる可能性があります。GC は世代別のガベージ コレクターであるため、オブジェクトが存在してからそれほど時間が経っていない場合、オブジェクトのクリーンアップにおいてもより効率的です。
一方、データベース接続オブジェクトを破棄しないと、プールされたデータベース接続がすぐに使い果たされ、アプリがクラッシュする可能性があります。
どちらを使用しても
using (new DisposableThing...
{
...
}
または、オブジェクト内の IDisposable への参照を存続期間中保持する必要がある場合は、オブジェクトに IDisposable を実装し、IDisposable の Dispose メソッドを呼び出します。
class MyClass : IDisposable
{
private IDisposable disposableThing;
public void DoStuffThatRequiresHavingAReferenceToDisposableThing() { ... }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
//etc... (see IDisposable on msdn)
}
一般に、IDisposable を実装するものはすべて、一時停止して、使用しているリソースを調査する必要があります。
GC はメモリ負荷が生じた場合にのみ発生するため、いつ発生するかを予測することはできません。ただし、AppDomain のアンロードによって確実にトリガーされます。
他の人も言っているように、使用することはあなたの友人です。私が書いた このブログエントリー 最も重要な部分を除外することでエラーが発生しにくくなる、非常に簡単な方法で IDisposable を実装する方法について説明します。
特定のオブジェクトが使い捨てリソースかどうか思い出せないときに私が使用するテクニックは、宣言の後に「.Dispose」(せいぜい!) と入力して Intellisense にチェックさせることです。
MemoryStream ms = new MemoryStream().Dispose
次に、.Dispose を削除し、using() ディレクティブを使用します。
using(MemoryStream ms = new MemoryStream())
{
...
}
まあ、リソースのマネージド バージョンを使用し、Windows API を自分で呼び出さない限り、問題ないはずです。.NET ではオブジェクトではなく「ウィンドウ ハンドル」 (およびその他の多くのもの) が認識されているため、取得したものが IntPtr である場合にのみ、リソースを削除/破棄する必要があることを心配する必要があります。
ちなみに、リソース (他の .NET オブジェクトと同様) は、現在のコンテキストから離れるとすぐにコレクションのフラグが設定されるため、メソッド内で Brush を作成した場合は、メソッドを終了するときにフラグが設定されます。
管理されている場合(つまり、フレームワークの一部)それについて心配する必要はありません。IDisposable を実装している場合は、それを using
ブロック。
アンマネージド リソースを使用したい場合は、ファイナライザーと IDisposable の実装について自分で読んでください。
詳細は以下にあります この質問
まず、プログラムが終了すると、プロセスによって使用されていたメモリがプロセス自体とともに削除されると想定できます。
.net で destroy または destructor を使用する場合、GC によって destroy 関数が呼び出される時刻は非決定的であることを理解する必要があります。このため、dispose を明示的に使用または呼び出して使用することをお勧めします。
ファイルなどのリソースを使用する場合、.net の管理された世界の外にあるセマフォーやリソースなどのメモリ オブジェクトを解放する必要があります。
たとえば、SolidBrush は GDI オブジェクトであり、.net 世界の外側にあるため、破棄する必要があります。
ガベージ コレクターは、プログラムの終了時に解放されるだけではありません。そうでなければ、実際には役に立ちません (まともな/最近の OS では、プロセスが終了すると、いずれにせよ、すべてのメモリが OS によって自動的にクリーンアップされます)。
C/C++ と比較した C# の大きな利点の 1 つは、割り当てられたオブジェクトの解放について (少なくともほとんどの場合) 気にする必要がないことです。gc はランタイムが決定したときにそれを実行します (いつ、どのように実行するかはさまざまな戦略によって決まります)。
多くのリソースは gc によって処理されません。ファイル、スレッド関連のリソース (ロック)、ネットワーク接続など...
注意すべき場所の 1 つは、オブジェクトです。 見て GC には小さいですが、そうではありません...たとえば、SharePoint API では、GC に関する限り SPWeb オブジェクトのフットプリントは小さいため、コレクションの優先順位は低くなりますが、実際には GC が取得できない大量のメモリ (ヒープ内にあると思われます) を取得しています。については知りません。たとえば、これらを大量に foreach する場合は、楽しいメモリの問題に遭遇することになります。using または destroy を常に使用することを忘れないでください。
オブジェクトを、解放する必要があるリソースを「保持している」ものとして考えるのではなく、オブジェクトを、そのオブジェクトよりも長く存続する何か (おそらくコンピュータの外部で) を変更したものとして考えるほうがよいでしょう。元に戻すことも「クリーンアップ」することもできませんが、オブジェクトのみがクリーンアップできます。この変更は通常、プール内の何らかの具体的なオブジェクトが「ビジー」とマークされるという形をとりますが、その正確な形式は重要ではありません。重要なのは、変更を元に戻す必要があり、そのために必要な情報がオブジェクトに保持されていることです。
ガベージ コレクターは、あらゆる管理リソースを処理します。あなたの例では、ガベージ コレクターが決定したときにブラシがクリーンアップされます。これは、ブラシへの最後の参照が有効でなくなってからしばらくしてから行われます。
手動でクリーンアップする必要があるものはいくつかありますが、それらは DLL 呼び出しなどのアンマネージ ソースから取得されたポインターであり、.NET Framework 内ではこの処理が必要なものはありません。