2016-02-12 12 views
9

MoveNext()を呼び出して次のコードブロックを実行できるので、列挙子とyieldキーワードを使用して非同期/互い違い操作を行うことができます。GCはIEnumeratorとどのように連携して処理されますか?

しかし、Enumeratorオブジェクトが何であるかは分かりません。列挙子のスコープを使用しているメモリはどこに行きますか?もしあなたがMoveNext()列挙子を終わらせなければ、最終的にGCになるのでしょうか?

基本的に、私は潜在的に多くの列挙子を使用しているため、GCのヒット数を下げようとしています。特にUnityの古いバージョンのためにGCがUnity内部で問題になる可能性があります。

私はこれをプロファイルしようとしましたが、まだ私の頭を包み込むことはできません。私はEnumeratorsで起こるスコープ/参照を理解していません。また、結果として得られる関数から列挙子を作成するときに列挙子がオブジェクトとして作成されるかどうかはわかりません。

次の例では、より良い私の混乱を示しています

// Example enumerator 
IEnumerator<bool> ExampleFunction() 
{ 
    SomeClass heavyObject = new SomeClass(); 
    while(heavyObject.Process()) 
    { 
     yield return true; 
    } 

    if(!heavyObject.Success) 
    { 
     yield return false; 
    } 

    // In this example, we'll never get here - what happens to the incomplete Enumerator 
    // When does heavyObject get GC'd? 
    heavyObject.DoSomeMoreStuff(); 
} 

// example call - Where does this enumerator come from? 
// Is something creating it with the new keyword in the background? 
IEnumerator<bool> enumerator = ExampleFunction(); 
while(enumerator.MoveNext()) 
{ 
    if(!enumerator.Current) 
    { 
     break; 
    } 
} 

// if enumerator is never used after this, does it get destroyed when the scope ends, or is it GC'd at a later date? 
+4

コンパイラは、列挙子メソッドを書き換えて、コードを名前を変更できないクラスに移動します。このクラスは、ローカル変数がクラスのフィールドになるステートマシンを実装し、状態を失うことなくMoveNext()を再入力できるようにします。 IEnumeratorインターフェイスリファレンスは、実際にはそのクラスのオブジェクトへの参照です。あなたのコードがforeachループを離れるとすぐにGC-edになることができるように、参照が収集されると、すべてが存在しなくなります。 –

+5

ハンスの言葉の例:http://goo.gl/fs4eNo – xanatos

+0

よろしくお願いいたします。それは私がAT ALLを期待したことをしません。 これは、列挙子が従来の手段に比べてかなり重いことを意味しますか?私はGCをダウンさせようとしていますが、新しいEnumeratorを起動するたびに新しいゴミを作成するようです(そして多くの列挙子があります)。 – mGuv

答えて

5

おそらく、列挙子の内部ポストを読むべきです。内部からこれらすべての質問に答えることができます。

クラッシュコース:各反復子メソッドの実行は、新しい列挙子オブジェクトを返します。ローカル変数はフィールドになります。

誰もその列挙子オブジェクトを使用していない場合、それは収集の対象となります。また、ローカル変数が作成していたすべての参照はGCの目的ではなくなりました。

正確にローカル変数がGC参照を停止するときに、いくつかのエッジケースがあります。あなたの列挙者がかなり短命である場合、これはあまり重要ではありません。

CLRは列挙子が何であるかを知りません。これはC#コンパイラによって生成されたクラスです。列挙子は、この後に使用されることはありませんhttp://goo.gl/fs4eNo

+0

そうですね、それはたくさん説明しています。私は、それがEnumeratorsになったときにどれくらいの自動化が行われていたかを認識していませんでした。 私はEnumeratorsを返すメソッドがたくさんあり、それらを呼び出す/使用することがたくさんあります。これは、生存するすべての列挙子がGCingを必要とするため、GCの問題になる可能性があると考えられます。それは私が何かをプロファイルする必要があります。 – mGuv

2

列挙子は、他のすべての管理インスタンスと同じように管理されている - それは範囲外であるとき。したがって、MoveNext()が決して呼び出されない場合、GCは、そのスコープ外にあるときに列挙子を削除します(または、削除のためにそれをよくマークします)。列挙子を反復することは並行して起こるわけではないので、実行とガベージコレクションの両方が確定的かつ連続的に実行されます。

イテレータメソッドの中で、yield-return-stamentが最後のステートメントであるとは限りません。つまり、MoveNext()が呼び出されると、現在の結果が返されます。しかしyield returnの後ろのすべてがMoveNextへのコールの後に実行されるので、DoSomeMoreStuffへの呼び出しも実行されます。

しかし、heavyObjectには範囲があります。したがって、イテレータは、while -loop内のブロックです。これは、イテレータがインスタンスを返さない場合でも、即座に処理されることを意味します。反復処理が完了すると、heavyObjectが即時処理されます。

3

//場合はスコープを とき、それが破壊されますん:それは説明のだと彼は答えを投稿していないようですので、この答えにザナトスでリンクを引っ張る

収集のための正しい

資格は何の参照を持っていない

を終了します。 GCのスコープについては、列挙子に特別なものは何もありません。列挙子は、他の型と同じようにスコープの外に出たときに廃棄/クリアされます。

+0

列挙子が関数内からローカルではなくローカルに参照されていない場合、安価に/即座にGC'dを取得できますか?私はGCを完全に理解していませんが、関数内のどこでも参照することはできないので、それをきれいにすることができることを検出するのはかなり簡単です。 – mGuv

+0

はい、正しいです。 – CharithJ

関連する問題