2009-07-02 5 views
21

내 소프트웨어에 두 개의 객체 인스턴스가 있고 그 중 하나는 다른 인스턴스에 가입되어 있습니다. 가비지 컬렉터가 정리하기 전에 고아가되기 전에 서로 구독을 취소해야합니까? 아니면 이벤트 관계를 정리해야하는 다른 이유가 있습니까? 객체에 등록 된 것이 고아이지만 구독자가 없거나 그 반대의 경우는 어떻게됩니까?개체가 고아되기 전에 이벤트 구독을 제거해야합니까?

답변

22

예. 이벤트 게시자는 개체에 대한 참조를 보유하고 있으며 가비지 수집을 방지합니다.

어떤 일이 발생하는지 예를 들어 봅시다. 우리에게는 두 가지 수업이 있습니다. 하나가 다른 그것을 소비하는 이벤트를 노출 : 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(); 

우리는 ClassB 인스턴스를 만들고 temp 변수를 통해이 인스턴스에 대한 참조를 보유합니다. 그것은 ClassA의 새로운 인스턴스를 전달합니다. 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

는 만 이벤트를 노출 객체 는 수명이 긴 경우 이벤트를 벗기기해야하지만 객체 이벤트가 달리 수명이 짧은 것 후킹 (쓰레기 신속 공정하게 수집된다).

긴 수명의 개체의 이벤트가 대리자를 보유하기 때문에 수명이 짧은 개체를 GCed 할 수 없기 때문에이 경우 후크 해제하지 않으면 메모리 누수가 발생합니다. 단명 개체에 대한 참조를 보유합니다. 단명 개체는 여전히 해당 대리인에 의해 참조되므로 가비지 수집 할 수 없습니다.

정적 이벤트는 정의에 따라 오래 지속됩니다. 정적 이벤트는 프로그램이 종료 될 때까지 유지됩니다. 정적 이벤트를 후킹하는 경우, 작업이 끝나면 그것을 언 휴크해야합니다.

두 개체가 모두 고아가 될 예정이면 언 후크가 필요하지 않습니다.

5

이벤트에 가입하면 구독자에 대한 강력한 참조가됩니다. 이것은 커버 아래에 이벤트가 델리게이트이고 인스턴스 메서드에 대한 델리게이트가 객체 참조와 실제 메소드의 조합이기 때문입니다. 구독을 취소하지 않으면 게시자가 참조를 계속 유지하며 구독자는 게시자가 살아있는 한 실제로 고아가되지 않습니다 (GC'ed).

반대의 경우는 사실이 아닙니다. 즉, 구독 된 개체에 게시자에 대한 참조가 없습니다.

관련 문제