c# - 警告 - idisposable dispose
IDisposableインターフェイスの適切な使用 (13)
私はIDisposable
インターフェイスの "プライマリ"の使用が管理されていないリソースをクリーンアップすることをMSDNのドキュメントから知っています。
私には、 "管理対象外"とはデータベース接続、ソケット、ウィンドウハンドルなどを意味しますが、 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万個の文字列が含まれていて、ガベージコレクタを待つのではなく、そのメモリを解放したいとします。 上記のコードはそれを達成するでしょうか?
Disposeが呼び出された後では、オブジェクトのメソッドへの呼び出しはもう必要ありません(オブジェクトはそれ以降のDisposeの呼び出しを許容する必要があります)。 したがって、その問題の例は愚かです。 Disposeが呼び出された場合、オブジェクト自体は破棄されます。 そのため、ユーザーはそのオブジェクト全体への参照をすべて破棄して(nullに設定する)、内部のすべての関連オブジェクトが自動的にクリーンアップされます。
マネージド/アンマネージドに関する一般的な質問と他の回答の議論については、この質問に対する回答は、アンマネージドリソースの定義から始める必要があると思います。
それは、システムを状態にするために呼び出すことができる関数があり、それをその状態から戻すために呼び出すことのできる別の関数があることです。 さて、典型的な例では、最初のものはファイルハンドルを返す関数であり、2番目のものはCloseHandle
への呼び出しです。
しかし、これが鍵です - それはどんな機能の一致するペアでもあります。 1つは状態を構築し、もう1つはそれを裂く。 状態が構築されているが、まだ切断されていない場合は、そのリソースのインスタンスが存在します。 適切なタイミングでティアダウンが行われるように手配しなければなりません。リソースはCLRによって管理されません。 自動的に管理されるリソースタイプはメモリのみです。 GCとスタックの2種類があります。 値の型はスタックによって管理されます(または参照型の内部に乗ることによって)、参照型はGCによって管理されます。
これらの関数は、自由にインターリーブすることができる状態変化を引き起こしたり、完全に入れ子にする必要があります。 状態の変化はスレッドセーフであるか、そうでない可能性があります。
正義の疑問の例を見てください。 ログファイルのインデントの変更は完全に入れ子になっていなければなりません。 また、スレッドセーフではありません。
ガベージコレクタを使用して乗車をして、管理されていないリソースをクリーンアップすることは可能です。 しかし、状態変更関数がスレッドセーフであり、2つの状態が重複する生存期間を持つことができる場合に限ります。 だからJusticeのリソースの例はファイナライザを持っていてはいけません! それは誰にも役立たないだろう。
これらのリソースの場合、ファイナライザなしでIDisposable
実装するだけで済みます。 ファイナライザは絶対にオプションです。 これは、多くの書籍で言及されていない、または言及されていない。
次に、 using
ステートメントを使用しusing
、 Dispose
が確実に呼び出されるようにする必要があります。 これは本質的にスタックとの乗り心地に似ています(ファイナライザーはGCにあり、スタックusing
ています)。
欠落している部分は、手作業でDisposeを書いてフィールドと基本クラスを呼び出さなければならないということです。 C ++ / CLIプログラマはそれをする必要はありません。 コンパイラは、ほとんどの場合、それらのために書き込みます。
代わりに、私は完全に入れ子状態で、スレッドセーフではない(IDisposableを実装するすべてのクラスにファイナライザを追加することができない人と議論するという問題を避けるために、IDisposableを避けて) 。
クラスを書く代わりに、関数を記述します。 この関数は、デリゲートを受け入れてコールバックします。
public static void Indented(this Log log, Action action)
{
log.Indent();
try
{
action();
}
finally
{
log.Outdent();
}
}
そして、簡単な例があります:
Log.Write("Message at the top");
Log.Indented(() =>
{
Log.Write("And this is indented");
Log.Indented(() =>
{
Log.Write("This is even more indented");
});
});
Log.Write("Back at the outermost level again");
渡されたラムダはコードブロックとして機能しますので、呼び出し元がそれを悪用する危険性がなくなったという点を除いて、 using
目的と同じ目的を果たす独自のコントロール構造を作成するようなものです。 リソースのクリーンアップに失敗することはありません。
このテクニックは、リソースが重複している可能性のある種類のリソースの場合、リソースAを構築してからリソースBを作成し、次にリソースAを強制終了し、後でリソースBを強制終了できるようにするためにはあまり役に立ちません。あなたが完全にこのようにネストするようにユーザーを強制した場合。 しかし、あなたはIDisposable
を使用する必要があります(ただし、スレッドセーフを実装していない限り、ファイナライザはまだありませんが、これは無料ではありません)。
Disposeのポイントは 、アンマネージドリソースを解放することです。 ある時点で実行する必要があります。そうしないと、決してクリーンアップされません。 ガベージコレクタは、 IntPtr
型の変数でDeleteHandle()
を呼び出す方法を知らないため、 DeleteHandle()
を呼び出す必要があるかどうかはわかりません。
注 : アンマネージドリソースとは何ですか? あなたがMicrosoft .NET Frameworkでそれを見つけた場合、それは管理されます。 自分でMSDNを掘り起こした場合、それは管理されていません。 あなたがP / Invoke呼び出しを使用して.NETフレームワークで利用可能なすべての素晴らしい快適な世界の外に出るために使用したものは、管理されていません - あなたは今それを掃除する責任があります。
作成したオブジェクトは、管理されていないリソースをクリーンアップするために、外部から呼び出せるメソッドを公開する必要があります。 このメソッドは好きなように名前を付けられます:
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で管理されている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は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;
}
}
だからあなたが必要とするものは、 Dispose()
管理対象リソースに触れてはいけないということ(これ以上は存在しない可能性があるためFinalize()
をFinalize()
に伝える方法です。
これを行うための標準的なパターンは、 Finalize()
とDispose()
両方で3番目の (!)メソッドを呼び出すことです。 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 Dispose()
{
try
{
Dispose(true); //true: safe to free managed resources
}
finally
{
base.Dispose();
}
}
そして、 あなたがより良いことができることを除いて 、すべてが良いです!
ユーザーがオブジェクトに対してDispose()
を呼び出すと、すべてがクリーンアップされます。 その後、ガベージコレクタが来て、Finalizeを呼び出すと、もう一度Dispose
を呼び出します。
これは無駄なだけでなく、 Dispose()
最後の呼び出しで既に処分したオブジェクトへのオブジェクトの参照がDispose()
場合は、それらを再度処分しようとします。
私のコードでは、私が処分したオブジェクトへの参照を慎重に削除していることに気づくでしょう。したがって、私は捨てられたオブジェクト参照に対してDispose
を呼び出そうとしません。 しかし、それは微妙なバグが忍び寄るのを止めませんでした。
ユーザーがDispose()
呼び出すと、ハンドルCursorFileBitmapIconServiceHandleが破棄されます。 後でガベージコレクタが実行されると、同じハンドルを再度破棄しようとします。
protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
{
//Free unmanaged resources
Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy
...
}
これを修正する方法は、ガベージコレクタに、オブジェクトをファイナライズする必要はなく、リソースはすでにクリーンアップされており、それ以上の作業は不要です。 これを行うには、 Dispose()
メソッドでGC.SuppressFinalize()
を呼び出します。
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.
}
問題は、ガベージコレクタがオブジェクトを完成させるときに気づかないことです。 ガベージコレクタが最終的に実行されるまで、管理されていない不要な未使用のネイティブリソースは固執します。 それからあなたのfinalizerメソッドを呼び出します。 管理されていないリソースをクリーンアップします。 Object.Finalizeのドキュメントではこれを指摘しています:
ファイナライザが実行される正確な時刻は未定義です。 クラスのインスタンスに対するリソースの確定的なリリースを確実にするには、 Closeメソッドを実装するか、
IDisposable.Dispose
実装を提供してください。
これは、 Dispose
を使用してアンマネージリソースをクリーンアップすることの利点です。 管理されていないリソースがクリーンアップされると、あなたは知り合い、制御されます。 それらの破壊は「決定論的」である 。
あなたのオリジナルの質問に答える:GCがそれをすることを決定したときではなく、今すぐメモリを解放してみてはどうですか? 私は今や 530 MBの内部画像を取り除く必要がある顔認識ソフトウェアを持っています。これはもはや必要ではないからです。 私たちがしなければ、マシンはスワッピングの停止に挽く。
ボーナスレディング
この答えのスタイルが好きな人には( 理由を説明し、 どのように明白になるか )、Don BoxのEssential COMの第1章をお読みになることをお勧めします。
- 直接リンク: Pearson Publishingによる第1章サンプル
- 磁石:84bf0b960936d677190a2be355858e80ef7542c0
35ページには、バイナリオブジェクトの使用の問題点を説明し、目の前にCOMを発明します。 COMの理由を理解すると、残りの300ページが明白であり、マイクロソフトの実装を詳しく説明します。
オブジェクトやCOMを扱ったプログラマは、少なくとも最初の章を読むべきだと思います。 それはこれまでの何の最善の説明です。
余分なボーナスレディング
あなたが知っているすべてが間違っているときはエリック・リッペルト
したがって、実際には正しいファイナライザを書くことは非常に難しく、 私があなたに与えることができる最良のアドバイスは試してはいけないことです 。
IDisposable
イベントから退会するのに適しています。
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.");
}
}
}
シナリオIDisposableを使用する:管理されていないリソースのクリーンアップ、イベントの登録解除、接続のクローズ
IDisposable( スレッドセーフではない )を実装するために使用するイディオム:
class MyClass : IDisposable {
// ...
#region IDisposable Members and Helpers
private bool disposed = false;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing) {
if (!this.disposed) {
if (disposing) {
// cleanup code goes here
}
disposed = true;
}
}
~MyClass() {
Dispose(false);
}
#endregion
}
管理リソースを廃棄するための最も正当な使用事例は、他の方法では収集されないリソースを回収するGCの準備です。
代表的な例は循環参照です。
循環参照を避けるパターンを使用することがベストプラクティスですが、(例えば)「親」への参照を持つ「子」オブジェクトで終了すると、放棄したばかりの親のGC収集を停止することができます参照とGCに依存+プラスファイナライザを実装している場合、それは決して呼び出されません。
これを回避する唯一の方法は、循環参照を手動で破棄することです。
これを行うには、親と子にIDisposableを実装するのが最善の方法です。Disposeが親に対して呼び出され、すべての子に対してDisposeを呼び出し、子Disposeメソッドで親参照をnullに設定します。
あなたの与えられたコードサンプルは、IDisposable
使い方の良い例ではありません。ディクショナリのクリアは通常、Dispose
メソッドには行かないでください。ディクショナリ項目は、範囲外になるとクリアされ、処分されます。IDisposable
範囲外になった後でも解放/解放されないメモリ/ハンドラを解放するための実装が必要です。
次の例は、いくつかのコードとコメントを持つIDisposableパターンの良い例を示しています。
public class DisposeExample
{
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class MyResource: IDisposable
{
// Pointer to an external unmanaged resource.
private IntPtr handle;
// Other managed resource this class uses.
private Component component = new Component();
// Track whether Dispose has been called.
private bool disposed = false;
// The class constructor.
public MyResource(IntPtr handle)
{
this.handle = handle;
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
component.Dispose();
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
CloseHandle(handle);
handle = IntPtr.Zero;
// Note disposing has been done.
disposed = true;
}
}
// Use interop to call the method necessary
// to clean up the unmanaged resource.
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~MyResource()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}
public static void Main()
{
// Insert code here to create
// and use the MyResource object.
}
}
このDispose()
サンプルコードでは、オブジェクトの通常のGCのために発生しない効果がある可能性がある操作がありMyCollection
ます。
オブジェクトが他のオブジェクトによって参照_theList
または参照されている場合、そのオブジェクトまたはオブジェクトはコレクションの対象にはなりませんが、突然コンテンツはありません。この例のようにDispose()操作がなかった場合でも、それらのコレクションには内容が残っています。_theDict
List<>
Dictionary<>
これは私が壊れたデザイン、それを呼ぶような状況だった場合はもちろん、 -私はちょうどことを(私が思う、pedantically)指摘していますDispose()
操作は、他の用途があるかどうかに応じて、完全に冗長ではないかもしれませんList<>
か、Dictionary<>
ではありませんその断片に示されている。
もちろん、そのコードは完全に冗長で不要なので、ガベージコレクタはそれ以外の方法では行えません(MyCollectionのインスタンスがスコープ外になると、それが処理されます).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()
。しかし、ここでこれを行う理由はありません。メモリは必要なときに解放されます。
何かあれば、コードを放置するよりも効率が悪くなると思います。
Clear()メソッドを呼び出すことは不要で、Disposeがそれをしなかった場合、GCはおそらくそれをしません...
投稿した例では、まだ「メモリを解放していません」。すべてのメモリはガベージコレクトされていますが、以前のgenerationでメモリが収集される可能性があります。あなたは確かにいくつかのテストを実行する必要があります。
フレームワーク設計ガイドラインはガイドラインであり、ルールではありません。インターフェースは、主にインターフェースが何であるか、いつ使用するか、使用方法、使用しない時期を教えてくれます。
私はかつてIDisposableを利用して失敗した場合に単純なRollBack()だったコードを読んでいました。以下のMiniTxクラスは、Dispose()のフラグをチェックし、Commit
呼び出しが起こらなかった場合は、Rollback
それ自身を呼び出します。インダイレクションのレイヤーが追加され、呼び出しコードを理解して維持するのをより簡単にしました。結果は次のようになりました。
using( MiniTx tx = new MiniTx() )
{
// code that might not work.
tx.Commit();
}
私は、タイミング/ロギングコードも同じことを見てきました。この場合、Dispose()メソッドはタイマーを停止し、ブロックが終了したことを記録しました。
using( LogTimer log = new LogTimer("MyCategory", "Some message") )
{
// code to time...
}
したがって、管理されていないリソースのクリーンアップを行わない具体的な例がいくつかありますが、IDisposableを使用してよりクリーンなコードを作成することは成功しています。
最初の定義。私にとっては、管理されていないリソースとは、IDisposableインターフェイスやdllの呼び出しを使って作成された何らかのクラスを意味します。GCはそのようなオブジェクトの処理方法を知らない。クラスに値の型だけがある場合は、このクラスを管理されていないリソースのクラスとはみなしません。私のコードでは、私は次の慣行に従います:
- 私が作成したクラスでは、いくつかの管理されていないリソースが使用されると、メモリを消去するためにIDisposableインターフェイスも実装する必要があります。
- オブジェクトの使用が終了するとすぐにオブジェクトをクリーンアップします。
- 私の処理メソッドでは、クラスのすべてのIDisposableメンバーを繰り返し、Disposeを呼び出します。
- 私のDisposeメソッドでGC.SuppressFinalize(this)を呼び出して、ガベージコレクタにオブジェクトがすでにクリーンアップされたことを通知します。GCの呼び出しは高価な操作なので、私はそれを行います。
- 私は、Dispose()を複数回呼び出すことを可能にしようとしています。
- ときどきプライベートメンバ_disposedを追加し、オブジェクトがクリーンアップされたかどうかのメソッド呼び出しをチェックインします。それがクリーンアップされた場合は、ObjectDisposedException を生成します。
次のテンプレートは、私が単語の中でコードのサンプルとして示したことを示しています。
public class SomeClass : IDisposable
{
/// <summary>
/// As usually I don't care was object disposed or not
/// </summary>
public void SomeMethod()
{
if (_disposed)
throw new ObjectDisposedException("SomeClass instance been disposed");
}
public void Dispose()
{
Dispose(true);
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)//we are in the first call
{
}
_disposed = true;
}
}
私は、管理されていないリソースを使用したり解放したりすることについて、通常のことを繰り返すことはありません。しかし、私はよくある誤解のようなものを指摘したいと思います。
与えられたコード
Public Class LargeStuff Implements IDisposable Private _Large as string() 'Some strange code that means _Large now contains several million long strings. Public Sub Dispose() Implements IDisposable.Dispose _Large=Nothing End Sub
Disposable実装は現在のガイドラインに従わないが、皆さんがすべてのアイデアを得ることを願っています。
今、Disposeが呼び出されると、どれくらいのメモリが解放されますか?
答え:なし。
Disposeを呼び出すと、アンマネージリソースを解放することができます。マネージメモリを再利用することはできません.GCだけがそれを行うことができます。上記のパターンに従えば、上記が良い考えではないと言っていないことは、実際にはまだまだ良いアイデアです。 Disposeが実行されると、LargeStuffのインスタンスがまだスコープ内にあっても、GCが_Largeによって使用されていたメモリの再要求を停止することはありません。 _Largeの文字列もgen 0にある可能性がありますが、LargeStuffのインスタンスはgen 2である可能性があります。この場合も、メモリはすぐに再要求されます。
しかし、上に示したDisposeメソッドを呼び出すファイナライザを追加する必要はありません。それはファイナライザを実行できるようにメモリの再要求を遅らせるだけです。