Disposeの目的は、アンマネージリソースを解放することです。ある時点で実行する必要があります。そうしないと、クリーンアップされません。ガベージコレクターは、型の変数を呼び出す方法を認識していません。呼び出す必要があるかどうかは認識していません。DeleteHandle()
IntPtr
DeleteHandle()
注:アンマネージリソースとは何ですか?Microsoft .NET Frameworkで見つかった場合は、管理されています。自分でMSDNを調べていると、管理されません。P / Invoke呼び出しを使用して.NET Frameworkで利用可能なすべての快適な世界の外に出たものはすべて管理対象外であり、これをクリーンアップする責任があります。
あなたが公開するニーズを作成したことをオブジェクトいくつかのアンマネージリソースをクリーンアップするために、外の世界を呼び出すことができるという、方法を。メソッドには任意の名前を付けることができます。
public void Cleanup()
または
public void Shutdown()
しかし、代わりにこのメソッドには標準化された名前があります:
public void Dispose()
作成されたインターフェースもありIDisposable
、それはその1つのメソッドだけを持っています。
public interface IDisposable
{
void Dispose()
}
したがって、オブジェクトにIDisposable
インターフェイスを公開させ、その方法で、アンマネージリソースをクリーンアップするための単一のメソッドを作成したことを約束します。
public void Dispose()
{
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
}
これで完了です。あなたがもっとうまくできることを除いて。
オブジェクトが何らかの種類のフレームバッファーとして250MBのSystem.Drawing.Bitmap(つまり.NETマネージビットマップクラス)を割り当てている場合はどうなりますか?確かに、これはマネージ.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は知っている、または約ケア、しないあなた のDisposeメソッド。これは、管理されていないものを削除するときに呼び出すメソッドに選択した名前にすぎません。
ガベージコレクターによるオブジェクトの破棄は、厄介なアンマネージリソースを解放する絶好の機会です。これを行うには、Finalize()
メソッドをオーバーライドします。
注: C#では、Finalize()
メソッドを明示的にオーバーライドしません。あなたはその方法を書くようになります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()
)から呼び出しているかどうかを示すブール値を渡します。つまり、管理対象リソースを解放しても安全です。
この内部メソッドには、「CoreDispose」や「MyInternalDispose」などの任意の名前を付けることができますが、それを呼び出すのは慣習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.
}
これの問題は、ガベージコレクターがいつオブジェクトをファイナライズするのかわからないことです。管理されていない、不要な、使用されていないネイティブリソースは、ガベージコレクターが最終的に実行されるまで残ります。次に、ファイナライザメソッドを呼び出します。アンマネージリソースをクリーンアップしています。Object.Finalizeのドキュメントはこれを指摘しています:
ファイナライザが実行される正確な時間は定義されていません。クラスのインスタンスのリソースを確実に解放するには、Closeメソッドを実装するか、IDisposable.Dispose
実装を提供します。
これは、Dispose
管理されていないリソースのクリーンアップに使用することの利点です。管理されていないリソースがいつクリーンアップされるかを知り、制御することができます。それらの破壊は「決定論的」です。
元の質問に答えるには、GCがメモリを解放することを決定したときではなく、今すぐメモリを解放しないのはなぜですか。現在、530 MBの内部画像を必要としないため、これを取り除く必要がある顔認識ソフトウェアを持っています。そうしないと、マシンはスワッピングの停止にグラインドします。
ボーナスリーディング
この回答のスタイルが好きな人は(理由を説明し、その方法が明らかになるので)、Don BoxのEssential COMの第1章を読むことをお勧めします。
35ページで、彼はバイナリオブジェクトの使用の問題を説明し、目の前でCOMを発明しています。COM の理由を理解したら、残りの300ページは明らかであり、Microsoftの実装の詳細を説明します。
オブジェクトやCOMを扱ったことのあるすべてのプログラマは、少なくとも最初の章を読むべきだと思います。それはこれまでで最も良い説明です。
追加ボーナス読書
Eric Lippert が知っているすべてが間違っているとき
したがって、正しいファイナライザを作成することは実際には非常に困難です。私ができる最善のアドバイスは、を試さないことです。