c#/xna のレンダー ターゲットは自動的に破棄されず、それらを削除するには .dispose() メンバーを呼び出す必要があることがわかりました。
ガベージ コレクションは、すべての参照がなくなった後に自動的に物を取り除くものだと思っていましたが、何が得られますか?
自動的に破棄されないものは他にありますか?
c#/xna のレンダー ターゲットは自動的に破棄されず、それらを削除するには .dispose() メンバーを呼び出す必要があることがわかりました。
ガベージ コレクションは、すべての参照がなくなった後に自動的に物を取り除くものだと思っていましたが、何が得られますか?
自動的に破棄されないものは他にありますか?
ガベージ コレクションは、すべての参照がなくなった後に自動的に物を取り除くものだと思っていましたが、何が得られますか?
これは 2 つの点で正しくありません。
ガベージ コレクションは、安全に収集でき、必要なマネージド メモリを収集します。それでおしまい。
ガベージ コレクションは、無限ヒープ メモリをシミュレートする方法と考えてください。無限のメモリがあるふりをすることができるので、使い終わったメモリを解放するために何かを呼び出す必要はありません。
GC が無限ヒープをシミュレートする最も簡単な方法は、何もしないことです。これは、たとえば、プロセスで 4GiB のメモリが利用可能で、50MiB を使用する場合に機能します。収集は決して起こりません。これは、コレクションが発生しないほどアプリが小さい場合に実際に発生します。(コレクションなしで megs を使用できるようにするほど怠惰ではありませんが、OS にアプリ用のメモリを追加するように要求する前に収集を試みますが、「なぜ GC を使用しなかったのか」と疑問に思っている場合に役立ちます。 .." 何もしないことが有効な GC アプローチである場合もありますが、その可能性を念頭に置いておけば、他の多くの質問はなくなります)。
別のアプローチは、クリーンアップできる瞬間にすべてを熱心にクリーンアップすることです。これは、参照カウント ガベージ コレクタで発生します。これは.NETとは何の関係もありませんが、あなたの質問の「すべての参照がなくなった後」と密接に一致するため、言及する価値があります。
もう 1 つは、メモリが必要であり、既に解放されているメモリのストアで使用できない場合、GC はすべてのスレッドを停止し、ルート (静的変数と各スレッドのスタック内のローカル参照) を識別し、ルートによって参照されるすべてのものを識別します。そして、それらによって参照されるすべてのものなど、アプリケーションによってまだ実行可能に使用される可能性があるすべてが見つかるまで続き、それ以外のすべてによって使用されるメモリは空きと見なされます。次に、メモリが解放されていないすべてのオブジェクトを圧縮します。これにより、より多くの空きメモリを提供する際の断片化が回避され、多くの場合、オブジェクトがメモリ内でより近くに保持されます (パフォーマンス上のわずかな利点があります)。
削除しなかったオブジェクトも「昇格」する場合、最初に削除するのが正しくなかった可能性があるため、次回はそうではないため、このプロセスを生き残ったオブジェクトをあまり調べません。頻繁。
この時点で注意すべき点は次の 2 つです。
もちろん、マネージ メモリを取得してから解放することは、最初に何かを実行してから元に戻したい場合の 1 つのケースにすぎません。その他の例は次のとおりです。
これまで説明してきた GC は、これらのいずれにも役立ちません。
それでも、それらにはすべて2つの共通点があります。開始操作と終了操作があります。
開始操作は、オブジェクトの作成または何らかのメソッド呼び出しに適切にマップされます。
Close()
終了操作は、End()
、Free()
、Release()
メソッド呼び出しに一致する可能性がありますが、定義IDisposable.Dispose()
では、それらすべてに共通のインターフェイスを与えることができます。using
言語は、 †を介していくつかのヘルプを追加することもできます。
(クラスは aClose()
と a の両方Dispose()
を持つことができます。この場合、後で再度開くか、閉じた状態で使用するものを閉じるオプションと、終了後にクリーンアップを保証する方法の両方があります。物体)。
このように、マネージ メモリを除いIDisposable.Dispose()
て、クリーンアップが必要なすべてのものをクリーンアップするために存在します。
さて、この場合、実装する必要がある 3 種類のクラスがありますIDisposable
。
IDisposable
ため、このクラスのオブジェクトを破棄するとDispose()
、それらのフィールドが呼び出されます。Dispose()
GC がそのようなオブジェクトのメモリを解放し、呼び出されていない場合に何が起こるかを考えてみましょう。
3 番目のケースでは、オブジェクトが破棄されていなくても問題ありません。本当に重要なのは、フィールドが破棄されていないことです (または、おそらくそれは問題ではありませんが、その下にあるフィールドが重要です)。
2 番目のケースでは、その重要性は、プーリングがどれほど重要であったかによって異なります。それはおそらく最適ではありませんが、世界の終わりではありません.
最初のケースでは、これは惨事です。アプリケーションが終了するまでリリースできない未リリースのリソースがあり、おそらくその後もリリースできません (リソースの性質によって異なります)。
このため、オブジェクトはファイナライザーを持つことができます。
GC がオブジェクトのメモリを解放しようとしているとき、ファイナライザーがあり、そのファイナライザーが抑制されていない場合 (Dispose()
通常、オブジェクトが適切にクリーンアップされ、それ以上の作業が必要ないことを示すためにこれを行います) )、オブジェクトからメモリを解放する代わりに、ファイナライズ キューに入れます。もちろん、これは、そのオブジェクトがメモリを収集していないだけでなく、そのフィールドを介して到達可能なオブジェクトも収集していないことを意味します。
ファイナライザー スレッドは、このキューを介して処理し、それぞれのファイナライザー メソッドを呼び出します。
これには 2 つの悪い点があります。
編集: 3 番目の種類のクラスにはファイナライザーがなく、おそらく 2 番目の種類ではないことに注意してください。この場合、ファイナライザーは必要ありません。なぜなら、フィールドとして持っていた本当に重要なオブジェクトには、重要な作業を行うファイナライザーが呼び出されるからです。また、ファイナライザー内からファイナライズ可能なフィールドを処理しようとすると、非常に簡単に致命的なバグが発生します。「所有」し、クリーンアップを担当する 1 つ以上の使い捨てフィールドをラップする使い捨てクラスを作成する場合は、 を実装IDisposable
しますが、ファイナライザーは追加しないでください。
全体として、呼び出されるファイナライザーは次の 2 つのいずれかを意味します。
そのため、ファイナライザーを介して GC とマネージ メモリ以外のリソースとの間で対話が行われますが、これは最後の手段の対話であり、決して信頼できるものではありません。ファイナライザーを GC にクリーンアップを行わせる方法と考えるべきではありませんが、GC がクリーンアップを不可能にしないための方法として (そしてクリーンアップを行う方法として) と考えるべきです。 -アプリケーションのシャットダウン時にアップ)。
*もちろん、無限の資源 (魚、水牛、海洋の廃棄物処理能力) があると思っていたのに、そうでないことが判明した場合、事態は混乱する可能性があるため、そのルールをすべてに適用しないでください。生活。
†<code>using を使用すると、呼び出しがDispose()
簡単になります。
using(someDisposableObject)
{
//Do Stuff
}
以下と同等です。
try
{
//Do Stuff
}
finally
{
if(someDisposableObject != null)
((IDisposable)someDisposableObject).Dispose();
}
と:
using(var someDisposableObject = someMethodCallOrCallToNew())
{
//Do Stuff
}
以下と同等です。
var someDisposableObject = someMethodCallOrCallToNew();
try
{
//Do Stuff
}
finally
{
if(someDisposableObject != null)
((IDisposable)someDisposableObject).Dispose();
}
最適化として、 someDisposableObject を null にすることは不可能であるとコンパイラが判断できる場合、null チェックを削除できます。
存在する理由IDisposable
は、ガベージコレクションがオブジェクトメモリを[効果的に]管理できない例外的なケースをサポートするためです。それが存在する主な理由は、管理されていないリソースを解放するためです。
これの一般的なケースの1つは、管理されていないメモリとの相互作用です。オブジェクトがガベージコレクターのスコープ外にメモリを割り当てることを含む何かをしているとき、ガベージコレクターはそれをクリーンアップする責任がありません。プログラマーが処理する必要があります。これらのケースでは、大量のメモリが割り当てられる可能性も高くなるため、メモリをより迅速にクリーンアップすることが非常に重要になります。
また、オブジェクトに、実際にメモリを解放する以外に実行する必要があるある種の「クリーンアップ」がある場合にも使用されます。例としては、IOを処理するときのファイルハンドラー、または閉じる必要のあるデータベースへの接続があります。ガベージコレクターにオブジェクトのメモリを解放させると、このタイプのクリーンアップは実行されません。
プログラマーがIDisposeable
インターフェースを使用してステートメントの構文を「ハイジャック」し、using
実際にはそのdisposeメソッドにアンマネージリソースが破棄されていない場合もあります。
Dispose
自動ガベージコレクションを使用する場合でも、インスタンスを再度使用しないことが確実な場合は、呼び出すことをお勧めします。
または、using
ステートメントを使用します。
そうすれば、ガベージコレクターが実行するまで待つのではなく、オブジェクトを「破棄」するタイミングを選択できます。
私の意見では、オブジェクトに Dispose メソッドがある場合は、おそらく破棄する必要があります。私は通常、「using」ステートメントを使用してブロックに入れようとします。これにより、ブロックの最後で Dispose メソッドが自動的に呼び出されます。