2009-07-02 11 views
21

私のソフトウェアに2つのオブジェクトインスタンスがあり、そのうちの1つがもう1つのオブジェクトインスタンスにサブスクライブされている場合。ガベージコレクタによって浄化されるために孤児になる前に、それらを互いに解約する必要がありますか?または、私がイベントの関係をクリアする理由が他にありますか?オブジェクトのサブスクライブが孤立していても、サブスクライバが孤立している場合、またはその逆の場合はどうなりますか?オブジェクトが孤立する前にオブジェクトからイベントサブスクリプションを削除する必要はありますか?

答えて

22

はい、あります。イベントの発行者はオブジェクトへの参照を保持していて、ガベージコレクションされないようにします。

例を見て、どうなるか見てみましょう。私たちには2つのクラスがあります。一方が他方がそれを消費し、イベントを公開します。ClassBのはにClassAのインスタンスへの参照を格納していませんか

class ClassA 
{ 
    public event EventHandler Test; 
    ~ClassA() 
    { 
     Console.WriteLine("A being collected"); 
    } 
} 
class ClassB 
{ 
    public ClassB(ClassA instance) 
    { 
     instance.Test += new EventHandler(instance_Test); 
    } 

    ~ClassB() 
    { 
     Console.WriteLine("B being collected"); 
    } 

    void instance_Test(object sender, EventArgs e) 
    { 
     // this space is intentionally left blank 
    } 
} 

注意。単にイベントハンドラをフックアップするだけです。

ここでオブジェクトの収集方法を見てみましょう。シナリオ1:

ClassB temp = new ClassB(new ClassA()); 
Console.WriteLine("Collect 1"); 
GC.Collect(); 
Console.ReadKey(); 
temp = null; 
Console.WriteLine("Collect 2"); 
GC.Collect(); 
Console.ReadKey(); 

クラスBインスタンスを作成し、temp変数を使用してそのインスタンスへの参照を保持します。 ClassAの新しいインスタンスが渡されます。そこにはどこにも参照が格納されないため、ClassBコンストラクタが完了した直後にスコープから外れます。 ClassAがスコープから外れたときと、ClassBがスコープから外れたときに一度実行されるガベージコレクタがあります。出力:

Collect 1 
A being collected 
Collect 2 
B being collected 

シナリオ2:

ClassA temp = new ClassA(); 
ClassB temp2 = new ClassB(temp); 
temp2 = null; 
Console.WriteLine("Collect 1"); 
GC.Collect(); 
Console.ReadKey(); 
temp = null; 
Console.WriteLine("Collect 2"); 
GC.Collect(); 
Console.ReadKey(); 

ClassAクラスの新しいインスタンスが作成され、それへの参照を一時変数に格納されています。 ClassBの新しいインスタンスが作成され、tempのClassAインスタンスが渡され、temp2にそのインスタンスへの参照が格納されます。次に、temp2をnullに設定して、ClassBインスタンスをスコープから外します。前述のように、各インスタンスがスコープ外になったらガベージコレクタを実行します。出力:

Collect 1 
Collect 2 
B being collected 
A being collected 

したがって、イベントを公開するインスタンスがスコープ外になると、イベントハンドラがフックアップされているかどうかにかかわらず、ガベージコレクションのために利用可能になります。イベントハンドラを持つインスタンスが別のインスタンスのイベントにフックアップされた場合、イベントハンドラがデタッチされるか、またはイベントハンドラがアタッチされるインスタンスがガベージコレクションに使用可能になるまで、ガベージコレクションに使用できなくなります。

9

はあなただけイベントを暴露オブジェクトは、長寿命である場合、イベントを外しする必要がありますが、オブジェクトは、イベントがそうでなければ短命だろうをフック(ゴミはかなり迅速に収集されます)。

この場合、アンリックに失敗すると、短命オブジェクトがGCできなくなるため、メモリリークが発生します。これは、長期保存オブジェクトのイベントがデリゲートに保持されるためです。短命オブジェクトへの参照を保持します。短命のオブジェクトは依然としてそのデリゲートによって参照されているので、ガベージコレクションされません。

静的イベントは、定義によって長く存続します。これらのイベントは、プログラムが終了するまで存続します。静的イベントをフックすると、完了したら間違いなくアンフックする必要があります。

両方のオブジェクトが孤立しようとしている場合は、アンフックは必要ありません。

5

イベントを購読すると、サブスクライバへの参照が強くなります。これは、表の下でイベントが代理人であり、インスタンスメソッドへの代理人がオブジェクト参照と実際のメソッドの組み合わせであるためです。登録を解除しないと、サイト運営者は引き続き参照を維持し、サイト運営者が生存している限り、登録しているオブジェクトは本当に孤立した状態になることはありません(GC'ed)。

逆のことは当てはまりません。つまり、サブスクライブされたオブジェクトにはパブリッシャへの参照がありません。

関連する問題