メソッド内でコードを実行するC#のタイマーがあります。コード内では、いくつかの一時オブジェクトを使用しています。
メソッド内にFoo o = new Foo();
のようなものがある場合、それはタイマーが作動するたびに新しいオブジェクトとそのオブジェクトへの新しい参照を作成しているということですか?
_string foo = null
_があり、fooに一時的なものを入れた場合、上記と同じですか?
ガベージコレクターはオブジェクトを削除しますが、参照またはオブジェクトは継続的に作成され、メモリに残りますか?
_Foo o;
_を宣言して、それをインスタンスに向けない場合、メソッドの終了時に破棄されませんか?
すべてを確実に削除したい場合、それを行う最善の方法は何ですか:
Foo o;
_をタイマーのメソッドの外に置き、内部で割り当てo = new Foo()
を行うだけで、メソッドの終了後にオブジェクトへのポインターが削除されると、ガベージコレクターはオブジェクトを削除します。1. Foo o = new Foo();のようなものがある場合メソッド内では、タイマーが作動するたびに、新しいオブジェクトとそのオブジェクトへの新しい参照を作成しているということですか?
はい。
2. string foo = nullがあり、fooに一時的なものを入れるだけの場合、上記と同じですか?
動作が同じかどうかを尋ねている場合は、はい。
3.ガベージコレクターはオブジェクトを削除しますが、参照またはオブジェクトは継続的に作成され、メモリに残りますか?
これらのオブジェクトが使用するメモリは、参照が未使用と見なされた後に最も確実に収集されます。
4. Foo oを宣言しただけの場合;インスタンスを指し示していない場合、メソッドの終了時に破棄されませんか?
いいえ、オブジェクトが作成されていないため、収集するオブジェクトはありません(破棄は正しいWordではありません)。
5.すべてが削除されていることを確認したい場合、それを行う最善の方法は何ですか
オブジェクトのクラスがIDisposable
を実装している場合、できるだけ早くDispose
を貪欲に呼び出したいでしょう。 using
キーワードは、Dispose
を例外セーフな方法で自動的に呼び出すため、これを簡単にします。
それ以外は、オブジェクトの使用を停止する場合を除いて、他に必要なことは何もありません。参照がローカル変数である場合、スコープから外れると、コレクションの対象になります。1 クラスレベルの変数の場合、null
を変数に割り当てて、包含クラスが適格になる前に適格にする必要があります。
1これは技術的に正しくありません(または少なくとも少し誤解を招く)。オブジェクトは、スコープから外れるずっと前にコレクションに適格になる可能性があります。 CLRは、参照が使用されなくなったことを検出したときにメモリを収集するように最適化されています。極端な場合、CLRはメソッドの1つがまだ実行中であってもオブジェクトを収集できます!
更新:
以下は、GCがまだスコープ内にある場合でもオブジェクトを収集することを示す例です。リリースビルドをコンパイルし、デバッガーの外部でこれを実行する必要があります。
static void Main(string[] args)
{
Console.WriteLine("Before allocation");
var bo = new BigObject();
Console.WriteLine("After allocation");
bo.SomeMethod();
Console.ReadLine();
// The object is technically in-scope here which means it must still be rooted.
}
private class BigObject
{
private byte[] LotsOfMemory = new byte[Int32.MaxValue / 4];
public BigObject()
{
Console.WriteLine("BigObject()");
}
~BigObject()
{
Console.WriteLine("~BigObject()");
}
public void SomeMethod()
{
Console.WriteLine("Begin SomeMethod");
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("End SomeMethod");
}
}
私のマシンでは、SomeMethod
の実行中にファイナライザーが実行されます!
.NETガベージコレクターがこれらすべてを処理します。
オブジェクトがいつ参照されなくなったかを判断でき、(最終的に)オブジェクトに割り当てられていたメモリを解放します。
オブジェクトは、一度ガベージコレクションの対象になります 範囲外に出る 到達不能になります(ありがとう!)。メモリは、ガベージコレクタがメモリ不足を認識していない限り解放されません。
管理対象リソースの場合、ガベージコレクターはこれがいつであるかを認識し、何もする必要はありません。
管理されていないリソース(データベースまたは開かれたファイルへの接続など)の場合、ガベージコレクターは、それらが消費しているメモリの量を知る方法がありません。
オブジェクトが解放されていない場合は、十分なメモリが残っていて必要がないか、アプリケーションでオブジェクトへの参照を維持しているため、ガベージコレクターはそれらを解放しません(実際にこの参照を使用する場合維持)
以下に簡単な概要を示します。
最後に、割り当てなくFoo foo;
を宣言する場合、心配する必要はありません-何もリークされません。 Fooが参照タイプの場合、何も作成されませんでした。 Fooが値型である場合、スタックに割り当てられるため、自動的にクリーンアップされます。
Foo
変数がメモリーを占有することはありません。using
ステートメントは、終了時にIDisposable
オブジェクトのdisposeを呼び出すだけなので、これは2番目の箇条書きに相当します。両方とも、オブジェクトの処理が完了したことを示し、GCにオブジェクトを手放す準備ができていることを伝えます。オブジェクトへの唯一の参照を上書きすると、同様の効果があります。質問に一つ一つ答えましょう。
ガベージコレクターが出現し、参照されなくなったものをクリーンアップします。 Foo
内にアンマネージリソースがない限り、Disposeを呼び出すか、その上でusingステートメントを使用しても、あまり役に立ちません。
これはまだC#であったため、これが当てはまると確信しています。しかし、私はXNAを使用してゲームデザインコースを受講し、C#のガベージコレクターについて話をしました。収集するオブジェクトへの参照があるかどうかを確認する必要があるため、ガベージコレクションは高価です。そのため、GCはこれをできる限り延期しようとします。したがって、プログラムが700MBに達したときに物理メモリが不足していない限り、GCが遅延していて、まだ心配していない可能性があります。
ただし、ループの外で_Foo o
_を使用し、毎回o = new Foo()
を作成するだけであれば、すべて正常に機能するはずです。
ブライアンが指摘するように、GCは、まだスコープ内にあるオブジェクトや、それらのオブジェクトのインスタンスメソッドがまだ実行中であっても、到達不能なものをすべて収集できます。次のコードを検討してください。
class foo
{
static int liveFooInstances;
public foo()
{
Interlocked.Increment(ref foo.liveFooInstances);
}
public void TestMethod()
{
Console.WriteLine("entering method");
while (Interlocked.CompareExchange(ref foo.liveFooInstances, 1, 1) == 1)
{
Console.WriteLine("running GC.Collect");
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine("exiting method");
}
~foo()
{
Console.WriteLine("in ~foo");
Interlocked.Decrement(ref foo.liveFooInstances);
}
}
class Program
{
static void Main(string[] args)
{
foo aFoo = new foo();
aFoo.TestMethod();
//Console.WriteLine(aFoo.ToString()); // if this line is uncommented TestMethod will never return
}
}
デバッグビルド、デバッガ、または指定された行のコメントなしで実行された場合、TestMethodは戻りません。ただし、TestMethodが接続されたデバッガなしで実行すると戻ります。