문제

Enter 키 누르기를 처리하고 다음 컨트롤로 이동할 수 있는 WPF 응용 프로그램에 "연결된 동작"을 만들었습니다.나는 그것을 EnterKeyTraversal.IsEnabled라고 부르며, 내 블로그에서 코드를 볼 수 있습니다. 여기.

이제 주요 관심사는 UIElements에서 PreviewKeyDown 이벤트를 처리하고 이벤트를 명시적으로 "해제"하지 않기 때문에 메모리 누수가 발생할 수 있다는 것입니다.

이 누출을 방지하는 가장 좋은 방법은 무엇입니까(실제로 누출이 있는 경우)?관리 중인 요소 목록을 유지하고 Application.Exit 이벤트에서 PreviewKeyDown 이벤트를 해제해야 합니까?자신의 WPF 애플리케이션에서 연결된 동작을 성공적으로 수행하고 우아한 메모리 관리 솔루션을 생각해낸 사람이 있습니까?

도움이 되었습니까?

해결책

동의하지 않습니다 DannySmurf

일부 WPF 레이아웃 개체는 가비지 수집되지 않을 때 메모리를 가득 채우고 애플리케이션 속도를 매우 느리게 만들 수 있습니다.따라서 단어 선택이 정확하다고 생각합니다. 더 이상 사용하지 않는 개체에 메모리가 누출되고 있습니다.항목이 가비지 수집될 것으로 예상하지만 어딘가에 참조가 있기 때문에(이 경우 이벤트 처리기에서) 그렇지 않습니다.

이제 실제 답변을 들어보세요 :)

이 글을 읽어보시길 권합니다 MSDN의 WPF 성능 문서

객체에서 이벤트 처리기를 제거하지 않으면 개체를 살리면

객체가 이벤트로 전달되는 대표는 사실상 그 객체에 대한 참조입니다.따라서 이벤트 핸들러는 객체를 예상보다 오래 유지할 수 있습니다.객체의 이벤트를 듣기 위해 등록 된 객체를 정리할 때 객체를 공개하기 전에 해당 대의원을 제거해야합니다.불필요한 물체를 살아 남기면 응용 프로그램의 메모리 사용이 증가합니다.이것은 물체가 논리적 트리 또는 시각적 트리의 뿌리 일 때 특히 그렇습니다.

그들은 당신에게 다음을 조사하라고 조언합니다. 약한 이벤트 패턴

또 다른 해결책은 객체 작업이 완료되면 이벤트 핸들러를 제거하는 것입니다.하지만 연결된 속성을 사용하면 그 점이 항상 명확하지 않을 수 있다는 것을 알고 있습니다.

도움이 되었기를 바랍니다!

다른 팁

철학적 논쟁은 제쳐두고 OP의 블로그 게시물을 보면 여기에 누출이 없습니다.

ue.PreviewKeyDown += ue_PreviewKeyDown;

하드 참조 ue_PreviewKeyDown 에 저장되어 있습니다 ue.PreviewKeyDown.

ue_PreviewKeyDownSTATIC 방법이고 그럴 수 없다. GCed.

하드 참조 없음 ue 저장 중이므로 저장을 방해하는 것이 없습니다. GCed.

그래서...누출은 어디에 있습니까?

예, 예전에는 메모리 누수가 완전히 다른 주제라는 것을 알고 있습니다.그러나 관리 코드에서는 메모리 누수라는 용어의 새로운 의미가 더 적절할 수 있습니다.

Microsoft는 이를 메모리 누수로 인정하기도 했습니다.

WeakEvent 패턴을 구현하는 이유는 무엇입니까?

이벤트를 듣게되면 메모리 누출이 발생할 수 있습니다.이벤트를 듣는 일반적인 기술은 소스의 이벤트에 처리기를 첨부하는 언어 별 구문을 사용하는 것입니다.예를 들어, C#에서는 해당 구문이 다음과 같습니다.Source.someevent += New Noventhandler (Myeventhandler).

이 기술은 이벤트 소스에서 이벤트 리스너에 이르기까지 강력한 참조를 만듭니다.일반적으로 리스너를 위해 이벤트 핸들러를 첨부하면 청취자는 소스의 객체 수명에 영향을받는 객체 수명을 갖게됩니다 (이벤트 핸들러가 명시 적으로 제거되지 않는 한).그러나 특정 상황에서는 청취자의 객체 수명이 현재 응용 프로그램의 시각적 트리에 속하는지 여부와 같은 다른 요인들에 의해서만 제어되기를 원할 수 있습니다.소스 객체 수명이 리스너의 객체 수명을 넘어 확장 될 때마다 일반 이벤트 패턴은 메모리 누출로 이어집니다.청취자는 의도 한 것보다 오래 살아남습니다.

우리는 드래그 앤 드롭이 가능한 대형 ToolWindows, 모든 유용한 기능 및 XBAP와 모두 호환되는 클라이언트 앱에 WPF를 사용합니다.하지만 가비지 수집되지 않은 일부 ToolWindows에서도 동일한 문제가 발생했습니다.이는 여전히 이벤트 리스너에 의존하고 있기 때문입니다.이제 창을 닫고 앱을 종료해도 문제가 되지 않을 수 있습니다.그러나 많은 명령이 포함된 매우 큰 ToolWindows를 생성하고 이러한 모든 명령이 계속해서 재평가되고 사람들이 하루 종일 응용 프로그램을 사용해야 한다면...난 너에게 말할 수있어..정말 앱의 메모리와 응답 시간을 방해합니다.

또한 청소가 필요한 일부 이벤트로 인해 일부 개체가 가비지 수집되지 않는다고 관리자에게 설명하는 것보다 메모리 누수가 있다고 관리자에게 설명하는 것이 훨씬 더 쉽습니다.)

@Nick 예, 동작이 첨부된 것은 정의에 따라 이벤트를 처리하는 요소와 동일한 개체에 있지 않다는 것입니다.

나는 WeakReference를 어떻게든 사용하는 데 답이 있다고 생각하지만, 이를 설명하는 간단한 코드 샘플을 본 적이 없습니다.:)

일반 이벤트 대신 "약한 이벤트 패턴"을 구현해 보셨나요?

  1. WPF의 약한 이벤트 패턴
  2. 약한 이벤트 패턴(MSDN)

John Fenton 게시물에 대한 내 의견을 설명하려면 여기에 내 대답이 있습니다.다음 예를 살펴보겠습니다.

class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        var b = new B();

        a.Clicked += b.HandleClicked;
        //a.Clicked += B.StaticHandleClicked;
        //A.StaticClicked += b.HandleClicked;

        var weakA = new WeakReference(a);
        var weakB = new WeakReference(b);

        a = null;
        //b = null;

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        Console.WriteLine("a is alive: " + weakA.IsAlive);
        Console.WriteLine("b is alive: " + weakB.IsAlive);
        Console.ReadKey();
    }


}

class A
{
    public event EventHandler Clicked;
    public static event EventHandler StaticClicked;
}

class B
{
    public void HandleClicked(object sender, EventArgs e)
    {
    }

    public static void StaticHandleClicked(object sender, EventArgs e)
    {
    }
}

당신이 가지고 있다면

a.Clicked += b.HandleClicked;

그리고 b만 null로 설정하면 약한 A와 약한 B에 대한 참조가 모두 살아있게 됩니다!a만 null로 설정하면 b는 살아 있지만 a는 살아 있지 않습니다(이는 John Fenton이 이벤트 제공자에 하드 참조가 저장되어 있다고 잘못 언급했음을 증명합니다. 이 경우 a).

이것은 나를 잘못된 결론으로 ​​이끌었습니다.

a.Clicked += B.StaticHandleClicked;

a의 인스턴스가 정적 핸들러에 의해 유지되기 때문에 누출이 발생할 수 있습니다.이는 사실이 아닙니다(내 프로그램을 테스트해 보세요).정적 이벤트 핸들러 또는 이벤트의 경우에는 그 반대입니다.당신이 쓴다면

A.StaticClicked += b.HandleClicked;

b에 대한 참조가 유지됩니다.

이벤트 참조 요소가 양식 컨트롤의 텍스트 상자와 같이 참조하는 개체에 있는지 확인하세요.아니면 예방할 수 없는 경우.전역 도우미 클래스에 정적 이벤트를 만든 다음 이벤트에 대한 전역 도우미 클래스를 모니터링합니다.이 두 단계를 수행할 수 없는 경우 WeakReference를 사용해 보십시오. 일반적으로 이러한 상황에는 완벽하지만 오버헤드가 발생합니다.

방금 귀하의 블로그 게시물을 읽었는데 약간 오해의 소지가 있는 조언을 얻은 것 같습니다, Matt.있는 경우 실제 메모리 새다 그렇다면 이는 .NET Framework의 버그이며 코드에서 반드시 수정할 수 있는 것은 아닙니다.

내 생각에 당신(그리고 당신 블로그의 포스터)이 실제로 여기서 말하고 있는 것은 실제로 누출이 아니라 지속적인 메모리 소비에 관한 것입니다.그것은 같은 것이 아닙니다.명확히 말하면, 누수된 메모리는 프로그램에 의해 예약된 후 버려지고(즉, 포인터가 매달려 있는 상태로 남아 있음) 이후에 해제될 수 없는 메모리입니다.메모리는 .NET에서 관리되므로 이론적으로 불가능합니다.그러나 프로그램에 대한 참조가 범위를 벗어나거나 가비지 수집 대상이 되는 것을 허용하지 않고 점점 더 많은 양의 메모리를 예약하는 것이 가능합니다.그러나 해당 메모리는 누출되지 않습니다.프로그램이 종료되면 GC는 이를 시스템에 반환합니다.

그래서.귀하의 질문에 대답하자면 실제로 여기에 문제가 있다고 생각하지 않습니다.확실히 메모리 누수가 없으며 코드에서 메모리 소비에 관한 한 걱정할 필요가 없다고 생각합니다.할당을 취소하지 않고 해당 이벤트 핸들러를 반복적으로 할당하지 않는 한(예: 한 번만 설정하거나 할당할 때마다 정확히 한 번 제거), 당신이하고있는 것 같습니다. 코드는 괜찮을 것입니다.

이것이 귀하의 블로그 포스터가 귀하에게 주려고 했던 조언인 것 같습니다. 그러나 그는 무서운 단어인 "누출"이라는 놀라운 작업을 사용했지만 많은 프로그래머가 관리되는 세계에서 이 단어의 실제 의미를 잊어버렸습니다.여기에는 적용되지 않습니다.

@아르튜러스:

...쓰레기가 수집되지 않았을 때 메모리를 막고 응용 프로그램을 실제로 느리게 만듭니다.

그것은 눈부시게 명백하며 나는 동의하지 않습니다.하지만:

... 당신은 더 이상 사용하지 않는 것을 반대하기 위해 메모리를 새기고 있습니다 ...그들에 대한 언급이 있기 때문입니다.

"메모리가 프로그램에 할당되고 해당 프로그램은 프로그램 논리 결함으로 인해 메모리에 액세스할 수 없게 됩니다."(Wikipedia, "메모리 누수")

프로그램이 액세스할 수 있는 객체에 대한 활성 참조가 있는 경우 정의에 따르면 메모리 누수는 아닙니다.누수란 객체가 더 이상 (귀하 또는 OS/프레임워크에) 액세스할 수 없으며 해제되지 않음을 의미합니다. 운영 체제의 현재 세션이 지속되는 동안.여기서는 그렇지 않습니다.

(의미론적 나치라서 죄송합니다...제가 좀 구식일 수도 있지만 누출은 매우 구체적인 의미를 갖습니다.요즘 사람들은 원하는 것보다 2KB의 메모리를 더 많이 소비하는 것을 의미하기 위해 "메모리 누수"를 사용하는 경향이 있습니다...)

그러나 물론 이벤트 핸들러를 해제하지 않으면 이벤트 핸들러가 연결된 개체는 종료 시 가비지 수집기가 프로세스 메모리를 회수할 때까지 해제되지 않습니다.그러나 이 동작은 귀하가 암시하는 것과는 달리 전적으로 예상되는 것입니다.객체가 회수될 것으로 예상되는 경우 이벤트 핸들러를 포함하여 참조를 활성 상태로 유지할 수 있는 모든 것을 제거해야 합니다.

진짜 진짜,

물론 당신 말이 맞아요..그러나 관리되지 않는 코드를 결코 건드리지 않는 완전히 새로운 세대의 프로그래머가 이 세상에 태어나고 있으며, 저는 언어 정의가 계속해서 스스로를 재창조할 것이라고 믿습니다.WPF의 메모리 누수는 이러한 방식으로 C/Cpp와 다릅니다.

아니면 내 매니저들한테는 메모리 누수라고 하더군요..동료 동료들에게 나는 그것을 성과 문제라고 언급했습니다!

Matt의 문제를 언급하면 ​​해결해야 할 성능 문제일 수 있습니다.몇 개의 화면만 사용하고 해당 화면 컨트롤을 싱글톤으로 만드는 경우 이 문제가 전혀 표시되지 않을 수 있습니다.

뭐 (매니저쪽은) 확실히 이해하고 공감합니다.

그러나 Microsoft가 이를 무엇이라고 부르든 "새로운" 정의는 적절하지 않다고 생각합니다.우리는 100% 관리되는 세계에 살고 있지 않기 때문에 복잡합니다(Microsoft는 우리가 관리하는 것처럼 가장하기를 좋아하지만 Microsoft 자체는 그러한 세상에 살고 있지 않습니다).메모리 누수라고 하면 프로그램이 너무 많은 메모리를 소비하고 있거나(이것이 사용자 정의임) 관리되는 참조가 종료될 때까지 해제되지 않거나(여기서와 같이) 관리되지 않는 참조가 제대로 정리되지 않음을 의미할 수 있습니다. up(실제 메모리 누수) 또는 관리 코드에서 호출된 비관리 코드가 메모리 누수(또 다른 실제 누수)입니다.

이 경우 부정확하더라도 "메모리 누수"가 무엇을 의미하는지 분명합니다.그러나 모든 과잉 소비 또는 수집 실패를 메모리 누수라고 부르는 일부 사람들과 대화하는 것은 매우 지루합니다.그리고 이 사람들이 아마도 더 잘 알고 있다고 생각되는 프로그래머라면 실망스럽습니다.기술적인 용어가 명확한 의미를 갖는 것이 중요하다고 생각합니다.그렇게 하면 디버깅이 훨씬 쉬워집니다.

그래도.이것을 언어에 관한 바람이 잘 통하는 요정 토론으로 바꾸려는 뜻은 아닙니다.그냥 말인데...

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top