[解決済み] IDisposable インターフェースの正しい使用法
質問内容
を読んで知っています。
マイクロソフトのドキュメント
の主な用途は、「quot;primary" 」であることがわかりました。
IDisposable
インターフェースは、管理されていないリソースをクリーンアップするためのものです。
私にとって、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万個の文字列を含んでおり、そのメモリを解放したいとします。
今
ガベージコレクタを待つのではなく。 上記のコードで実現できるでしょうか?
解決方法は?
ディスポのポイント
は
管理されていないリソースを解放することです。これはある時点で実行される必要があり、そうでなければクリーンアップされることはありません。ガベージコレクタは
どのように
を呼び出すことができます。
DeleteHandle()
型の変数で
IntPtr
を知らないのであれば、それは
かどうか
を呼び出す必要があるかどうか。
DeleteHandle()
.
備考 : とは何ですか? アンマネージドリソース ? Microsoft .NET Frameworkで見つけたのなら、それはマネージドです。もし、あなたが自分でMSDNをうろついたのなら、それはアンマネージドです。P/Invokeコールを使って、.NET Frameworkの快適な世界の外に出たものは、アンマネージドであり、その後始末はあなたの責任になります。
作成したオブジェクトには いくつか このメソッドは、管理されていないリソースをクリーンアップするために、外部から呼び出すことができます。このメソッドには好きな名前を付けることができます。
public void Cleanup()
または
public void Shutdown()
しかし、その代わりにこのメソッドには標準的な名称があります。
public void Dispose()
インターフェースも作られていた。
IDisposable
そのメソッドだけを持つ
public interface IDisposable
{
void Dispose()
}
そこで、オブジェクトに
IDisposable
インターフェイスを使用することで、アンマネージドリソースをクリーンアップするためのメソッドをひとつだけ書いたことを約束することができます。
public void Dispose()
{
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}
で、終了です。 ただし、もっといいものができるはずだ。
オブジェクトが250MBを割り当てている場合はどうでしょう。 システム.ドローイング.ビットマップ (つまり、.NETで管理されたBitmapクラス)をある種のフレームバッファとして使用することはできますか? 確かに、これは管理された.NETオブジェクトであり、ガベージコレクタがそれを解放します。しかし、250MBのメモリをそのままにして、ガベージコレクタが解放するのを待ちますか? 結局 がやってきて、それを解放してくれるでしょうか?もし オープンデータベース接続 ? 確かに、GCがオブジェクトをファイナライズするのを待つために、その接続を開いたままにしておきたくはありません。
もしユーザーが
Dispose()
(つまり、もうこのオブジェクトを使う予定はない) 無駄なビットマップやデータベース接続を排除してはどうでしょう?
だから、今度はそうします。
- 管理されていないリソースを削除する(必要だから)、そして
- 管理されたリソースを取り除く(役に立ちたいから)。
それでは
Dispose()
メソッドを使用して、これらの管理対象オブジェクトを削除します。
public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//Free managed resources too
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose();
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose();
this.frameBufferImage = null;
}
}
そして、すべてがうまくいく。 ただし、もっといい方法がある !
もし、その人が
忘れた
を呼び出す
Dispose()
をオブジェクトの上に置くのですか?そうすると
アンマネージド
のリソースを使用することができます。
注 漏れることはありません マネージド なぜなら、最終的にはガベージコレクタがバックグラウンドスレッドで実行され、未使用のオブジェクトに関連するメモリを解放することになるからです。これには、あなたのオブジェクトや、あなたが使っているマネージドオブジェクト(たとえば
Bitmap
と、そのDbConnection
).
電話をかけ忘れた場合
Dispose()
を使用することができます。
今も
彼らのベーコンを救え!私たちはまだ、それを呼び出す方法があります
のために
ガベージコレクタが最終的にオブジェクトを解放する(つまり最終化する)ときです。
注意
ガベージコレクタは、最終的にすべての管理対象オブジェクトを解放します。
そのとき、ガベージコレクタは
Finalize
メソッドを実行します。GCはそのことを知りませんし、また
を気にする。
あなたの
廃棄する
というメソッドがあります。
これは私たちが選んだ名前で
を取得するときに呼び出すメソッドです。
は、管理されていないものを処分します。
ガーベジコレクタによるオブジェクトの破壊は
完璧
は、これらの厄介なアンマネージドリソースを解放するための時間です。これを行うには
Finalize()
メソッドを使用します。
注 C#では、明示的に
Finalize()
メソッドを使用します。 というメソッドを書きます。 のように見えます。 a C++のデストラクタ であり の実装とみなされます。Finalize()
メソッドを使用します。
~MyObject()
{
//we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
Dispose(); //<--Warning: subtle bug! Keep reading!
}
でも、そのコードにはバグがあるんだ。ご存知の通り、ガベージコレクタが実行されるのは
バックグラウンドスレッド
2つのオブジェクトが破壊される順番はわかりません。あなたの
Dispose()
のコードでは
マネージド
オブジェクトはもう存在しないのです。
public void Dispose()
{
//Free unmanaged resources
Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);
//Free managed resources too
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
this.frameBufferImage = null;
}
}
つまり、必要なのは
Finalize()
に伝える。
Dispose()
べきであることを
管理されたものには一切手をつけず
リソースは
いないかもしれない
を使用し、管理されていないリソースを解放しています。
これを実現するための標準的なパターンは
Finalize()
と
Dispose()
はどちらも
第三
(!) メソッドにブール値を渡します。
Dispose()
(とは対照的に
Finalize()
つまり、管理されたリソースを安全に解放することができるのです。
これは
内部
メソッド
かもしれない
のような任意の名前を付けることができますが、これは伝統的な呼び方です。
Dispose(Boolean)
:
protected void Dispose(Boolean disposing)
しかし、もっと親切なパラメータ名は、こうかもしれません。
protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//Free managed resources too, but only if I'm being called from Dispose
//(If I'm being called from Finalize then the objects might not exist
//anymore
if (itIsSafeToAlsoFreeManagedObjects)
{
if (this.databaseConnection != null)
{
this.databaseConnection.Dispose();
this.databaseConnection = null;
}
if (this.frameBufferImage != null)
{
this.frameBufferImage.Dispose();
this.frameBufferImage = null;
}
}
}
の実装を変更し
IDisposable.Dispose()
メソッドに変更します。
public void Dispose()
{
Dispose(true); //I am calling you from Dispose, it's safe
}
とファイナライザーを
~MyObject()
{
Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
}
備考 : を実装しているオブジェクトの子孫である場合、そのオブジェクトは
Dispose
を呼び出すことを忘れないでください。 ベース Disposeをオーバーライドする際に、Disposeメソッドを使用します。
public override void Dispose()
{
try
{
Dispose(true); //true: safe to free managed resources
}
finally
{
base.Dispose();
}
}
そして、すべてがうまくいく。 ただし、もっといい方法がある !
もしユーザーが
Dispose()
を実行すると、すべてがクリーンアップされます。その後、ガベージコレクタがやってきてFinalizeを呼び出すと、その時点で
Dispose
を再び使用します。
これは無駄であるだけでなく、もしあなたのオブジェクトが、すでに廃棄したオブジェクトへのジャンクリファレンスを
最後
を呼び出すと
Dispose()
を使うと、また廃棄しようとするでしょ!?
私のコードでは、ディスポーザブルにしたオブジェクトへの参照を削除するように注意していることにお気づきでしょう。
Dispose
をジャンクなオブジェクトの参照に置き換えます。しかし、それでも微妙なバグが忍び込むのを止めることはできませんでした。
ユーザーが
Dispose()
: ハンドル
CursorFileBitmapIconServiceHandle(カーソルファイルビットマップアイコンサービスハンドル
が破棄されます。その後、ガベージコレクタが実行されると、同じハンドルを再び破壊しようとします。
protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy
...
}
これを解決するには、ガベージコレクタに、わざわざオブジェクトをファイナライズする必要はない、そのリソースはすでにクリーンアップされており、これ以上の作業は必要ない、と伝えることです。これを行うには
GC.SuppressFinalize()
の中で
Dispose()
メソッドを使用します。
public void Dispose()
{
Dispose(true); //I am calling you from Dispose, it's safe
GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
}
これで、ユーザーが
Dispose()
は、あります。
- 解放されたアンマネージドリソース
- 解放されたマネージドリソース
GCがファイナライザーを実行する意味はありません。すべて片付きました。
Finalize を使って、管理されていないリソースをクリーンアップすることはできないのでしょうか?
のドキュメントでは
Object.Finalize
は言う。
Finalizeメソッドは、オブジェクトが破壊される前に、現在のオブジェクトが保持している非管理下のリソースに対してクリーンアップ処理を実行するために使用されます。
しかし、MSDNのドキュメントには、以下のように書かれています。
IDisposable.Dispose
:
管理されていないリソースの解放、解放、またはリセットに関連する、アプリケーション定義のタスクを実行します。
で、どっちなんだ?アンマネージド・リソースのクリーンアップを行うのはどちらでしょうか?その答えは、こうです。
<ブロッククオート
それはあなたの選択です! でも、選ぶのは
Dispose
.
確かに、ファイナライザーにアンマネージドクリーンアップを配置することは可能です。
~MyObject()
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
//A C# destructor automatically calls the destructor of its base class.
}
この場合の問題は、ガベージコレクタがいつあなたのオブジェクトをファイナライズするかわからないということです。管理されていない、必要とされていない、使われていないネイティブのリソースは、ガベージコレクタがそのオブジェクトをファイナライズするまで、ずっと残ってしまうのです。 結局 が実行されます。そして、ファイナライザー・メソッドが呼ばれ、管理されていないリソースが一掃されます。のドキュメントでは オブジェクトのファイナライズ は、このことを指摘しています。
ファイナライザが実行される正確な時刻は不定です。クラスのインスタンスのリソースを決定論的に解放することを確実にするために、以下のように 閉じる メソッドを提供するか
IDisposable.Dispose
の実装が必要です。
を使うことの良さです。
Dispose
を使用して、管理されていないリソースをクリーンアップします。管理されていないリソースがいつクリーンアップされるかを知ることができ、コントロールすることができます。その破壊は
決定論的である。
.
最初の質問に答えます。GCが決めた時のためではなく、なぜ今メモリを解放しないのか?私の持っている顔認識ソフトは が必要です。 530MBの内部イメージを取り除くために 現在 不要になったからです。そうしないと、マシンはスワップ停止に追い込まれる。
ボーナス・リーディング
この回答のスタイルが好きな人のために、( なぜ ということで どのように の第1章を読んでみてください。
- 直リンクです。 第1章サンプル by ピアソン出版
- magnet: 84bf0b960936d677190a2be355858e80ef7542c0
35ページにわたり、バイナリオブジェクトを使用する際の問題点を解説し、目の前でCOMを発明しています。一旦 なぜ 残りの300ページは、マイクロソフトの実装を詳細に説明するだけである。
オブジェクトやCOMを扱ったことのあるプログラマは、最低でも第1章を読むべきだと思います。これまでで最高の解説書だ。
おまけの読み物
あなたが知っていることがすべて間違っているとき アーカイヴ by Eric Lippert
そのため、正しいファイナライザーを書くのは実に難しい。 そして を試さないことが、私ができる最善のアドバイスです。 .
関連
-
[解決済み】「The breakpoint will not currently be hit」を改善するには?このドキュメントにはシンボルが読み込まれていません。" という警告はどうすれば改善されますか?
-
[解決済み】WPFでXamlファイルにコメントを追加する方法は?
-
[解決済み】なぜこのコードはInvalidOperationExceptionを投げるのですか?
-
[解決済み] C#のStringとstringの違いは何ですか?
-
[解決済み] C#の正しいバージョン番号を教えてください。
-
[解決済み] usingディレクティブはネームスペースの内側と外側のどちらを使うべきですか?
-
[解決済み] C#でベースコンストラクタを呼び出す
-
[解決済み] async」と「await」の使い方とタイミング
-
[解決済み] イールドリターン」の正しい使い方
-
[解決済み] System.gc()を呼び出すのはなぜ悪い習慣なのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] [Solved] 1つ以上のエンティティで検証に失敗しました。詳細は'EntityValidationErrors'プロパティを参照してください [重複]。
-
[解決済み】ここで「要求URIに一致するHTTPリソースが見つかりませんでした」となるのはなぜですか?
-
[解決済み】Excel "外部テーブルが期待された形式ではありません。"
-
解決済み] Critical error detected c0000374 - C++ dll returns pointer off allocated memory to C# [解決済み] Critical error detected c0000374 - C++ dll returns pointer off allocated memory to C#.
-
[解決済み】バックスラッシュを含むパス文字列のエスケープシーケンスが認識されない件
-
[解決済み】MetadataException: 指定されたメタデータ・リソースをロードできない
-
[解決済み】URLから画像をダウンロードする方法
-
[解決済み】インデックスが範囲外でした。コレクションパラメータname:indexのサイズより小さく、非負でなければなりません。
-
[解決済み】「namespace」なのに「type」のように使われる。
-
[解決済み】WebResource.axdとは何ですか?