https://www.spicelogic.com/Blog/net-event-handler-memory-leak-16 의 블로그에서 이러한 혼란을 설명했습니다 . 나는 당신이 명확한 생각을 가질 수 있도록 여기에 요약하려고 노력할 것입니다.
참조는 "필요함"을 의미합니다.
우선, 객체 A가 객체 B에 대한 참조를 보유하고 있다면 객체 A가 작동하려면 객체 B가 필요하다는 것을 이해해야합니다. 따라서 가비지 수집기는 개체 A가 메모리에 존재하는 한 개체 B를 수집하지 않습니다.
이 부분은 개발자에게 분명해야한다고 생각합니다.
+ = 왼쪽 개체에 오른쪽 개체의 참조를 주입하는 의미 :
그러나 혼란은 C # + = 연산자에서 비롯됩니다. 이 연산자는 개발자에게이 연산자의 오른쪽이 실제로 왼쪽 개체에 대한 참조를 주입하고 있다고 명확하게 알리지 않습니다.
그리고 그렇게함으로써, 객체 A는 객체 B가 필요하지만, 비록 객체 B가 존재하는지 아닌지는 상관하지 않아도 객체 B가 필요하다고 생각합니다. 객체 A는 객체 B가 필요하다고 생각하기 때문에 객체 A는 객체 A가 존재하는 한 가비지 수집기로부터 객체 B를 보호합니다. 그러나 이벤트 가입자 객체에 대한 보호 기능을 원하지 않으면 메모리 누수가 발생했다고 말할 수 있습니다.
이벤트 핸들러를 분리하여 이러한 누수를 피할 수 있습니다.
결정하는 방법?
그러나 전체 코드베이스에는 많은 이벤트와 이벤트 핸들러가 있습니다. 그것은 어디서나 이벤트 처리기를 분리해야한다는 것을 의미합니까? 정답은 '아니요'입니다. 그렇게해야한다면 코드베이스가 장황하게 보일 것입니다.
오히려 간단한 플로우 차트를 따라 분리 이벤트 핸들러가 필요한지 여부를 판별 할 수 있습니다.
대부분의 경우 이벤트 구독자 개체가 이벤트 게시자 개체만큼 중요하며 둘 다 동시에 존재해야합니다.
걱정할 필요가없는 시나리오의 예
예를 들어, 창의 단추 클릭 이벤트.
여기서 이벤트 게시자는 Button이고 이벤트 구독자는 MainWindow입니다. 해당 순서도를 적용하여 질문하십시오. 기본 창 (이벤트 구독자)이 단추 (이벤트 게시자)보다 먼저 종료되어야합니까? 분명히 아닙니다. 맞습니까? 심지어 말이되지 않습니다. 그렇다면 왜 클릭 이벤트 핸들러를 분리해야합니까?
이벤트 핸들러 분리가 필수 인 예입니다.
구독자 개체가 게시자 개체보다 먼저 종료되어야하는 한 가지 예를 제공하겠습니다. MainWindow가 "SomethingHappened"라는 이벤트를 게시하고 버튼 클릭으로 기본 창에서 하위 창을 표시합니다. 자식 창은 기본 창의 해당 이벤트를 구독합니다.
그리고 자식 창은 기본 창의 이벤트를 구독합니다.
이 코드를 통해 Main Window에 버튼이 있음을 분명히 이해할 수 있습니다. 해당 버튼을 클릭하면 자식 창이 나타납니다. 자식 창은 기본 창에서 이벤트를 수신합니다. 무언가를 한 후에, 사용자는 자식 창을 닫습니다.
이제 플로우 차트에 따르면 "이벤트 게시자 (메인 윈도우) 전에 자식 윈도우 (이벤트 구독자)가 죽어야합니까?" 나는 보통 언로드 된 윈도우 이벤트에서 그렇게한다.
경험 법칙: 뷰 (예 : WPF, WinForm, UWP, Xamarin Form 등)가 ViewModel의 이벤트를 구독하는 경우 항상 이벤트 핸들러를 분리해야합니다. ViewModel은 일반적으로 뷰보다 수명이 길기 때문입니다. 따라서 ViewModel이 파괴되지 않으면 해당 ViewModel의 이벤트를 구독 한 모든 뷰는 메모리에 남아 있으므로 좋지 않습니다.
메모리 프로파일 러를 사용한 개념 증명.
메모리 프로파일 러로 개념을 검증 할 수 없다면 재미가 없을 것입니다. 이 실험에서 JetBrain dotMemory 프로파일 러를 사용했습니다.
먼저 MainWindow를 실행했는데 다음과 같이 나타납니다.
그런 다음 메모리 스냅 샷을 찍었습니다. 그런 다음 버튼을 세 번 클릭했습니다 . 세 개의 자식 창이 나타났습니다. 모든 하위 창을 닫고 dotMemory 프로파일 러에서 Force GC 버튼을 클릭하여 가비지 콜렉터가 호출되는지 확인했습니다. 그런 다음 다른 메모리 스냅 샷을 만들어 비교했습니다. 보다! 우리의 두려움은 사실이었다. 가비지 수집기가 닫은 후에도 자식 창을 수집하지 않았습니다. 뿐만 아니라 ChildWindow 객체의 누수 된 객체 수에도 " 3 "이 표시됩니다 (버튼을 세 번 클릭하여 3 개의 자식 창을 표시합니다).
그런 다음 아래와 같이 이벤트 핸들러를 분리했습니다.
그런 다음 동일한 단계를 수행하고 메모리 프로파일 러를 확인했습니다. 이번에는와! 더 이상 메모리 누수가 없습니다.