티스토리 뷰

반응형

Memory Leak?

컴퓨터 과학에서 메모리 누수(memory leak)는 더 이상 필요하지 않은 메모리가 해제되지 않는 방식으로 컴퓨터 프로그램이 메모리 할당을 잘못 관리 할 때 발생하는 일종의 리소스 누수입니다. 객체가 메모리에 저장되어 있지만 실행중인 코드에서 액세스 할 수없는 경우에도 메모리 누수가 발생할 수 있습니다. 메모리 누수는 다른 여러 문제와 유사한 증상을 가지며 일반적으로 프로그램의 소스 코드에 액세스 할 수있는 프로그래머에 의해서만 진단 될 수 있습니다.

메모리 누수 셈플은 여기를 참고했습니다. 이 포스트에는 DotMemory, PerfView, OzCode를 이용하는 방법에 대해서도 설명이 있으니 참고하시면 좋을 것 같습니다.

메모리 누수 발생 샘플

MainWindow.xaml.cs

public partial class MainWindow : Window
    {
        List<ClockWindow> clocks = new();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Start_Click(object sender, RoutedEventArgs e)
        {
            for (int i = 0; i < 50; i++)
            {
                var clock = new ClockWindow();
                clock.Show();
                clocks.Add(clock);
            }
        }

        private void Stop_Click(object sender, RoutedEventArgs e)
        {
            foreach (var clock in clocks)
            {
                clock.Close();
            }
            clocks.Clear();
        }
    }

ClickWindow.xaml.cs

public partial class ClockWindow : Window
    {
        DispatcherTimer timer;

        public ClockWindow()
        {
            InitializeComponent();

            timer = new DispatcherTimer
            {
                Interval = new TimeSpan(0, 0, 1)
            };

            timer.Start();
            timer.Tick += UpdateTime;
        }

        private void UpdateTime(object sender, EventArgs e)
        {
            timerText.Content = DateTime.Now.ToLongTimeString();
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
        }
    }

Memory Usage를 이용해서 메모리 누수 확인

실행합니다.

오른쪽 하단에 Memory Usage 탭에서 사진기 아이콘 버튼을 눌러서 첫번째 메모리 스냅샷을 생성합니다.

Start 버튼을 눌러서 50개의 ClockWindow를 생성합니다.

Stop 버튼을 눌러서 모든 ClockWindow를 닫습니다. 그리고 사진기 버튼을 눌러서 두번째 스냅샷을 생성합니다.

Objects의 화살표 부분을 클릭합니다.

2번째 스냅샷의 정보를 조회할 수 있습니다. 

상단에 Compare to : 옆의 콤보박스를 눌러서 Snapshot #1을 선택하면 첫번째 스냅샷과 두번째 스냅샷을 비교해서 차이가 나는 부분만 표시합니다.

차이나는 부분만 표시

오른쪽 필터 부분에 clockwindow를 입력합니다.

이름에서 clockwindow가 포함된 object 목록을 볼 수 있습니다.

모든 ClockWindow를 닫았는데도, 아래와 같이 50개의 ClockWindow가 존재하는 것을 확인할 수 있습니다.

이렇게 접근할 수 없는 영역에 ClockWindow가 남아있는 현상을 메모리 누수라고 이야기를 하며, 대부분의 경우 이벤트 핸들러와 관련된 문제 입니다.

문제 해결

ClockWindow를 선택하면 하단에 상세 정보가 표시되는데, 그 중 EventHandler 부분을 확장하면 DispatcherTimer가 보이고, DispatcherOperationCallback이라는 것이 표시되는 것을 알 수 있습니다. 

만약, 여러개의 이벤트 핸들러가 있다면 각 각의 내용들을 모두 점검을 하면서 찾는 과정이 필요합니다.

이벤트 핸들러가 표시되지 않는다면, 화면 종료시에 사용했던 모든 객체를 null로 만들어준 후 확인을 하시면 됩니다.

ClockWindow.xaml.cs

OnClosed 메소드에 timer.Tick -= UpdateTime;을 추가합니다.

using System;
using System.Windows;
using System.Windows.Threading;

namespace CoreWpfMemory
{
    /// <summary>
    /// Interaction logic for ClockWindow.xaml
    /// </summary>
    public partial class ClockWindow : Window
    {
        private readonly DispatcherTimer timer;

        public ClockWindow()
        {
            InitializeComponent();

            timer = new DispatcherTimer
            {
                Interval = new TimeSpan(0, 0, 1)
            };

            timer.Start();
            timer.Tick += UpdateTime;
        }

        private void UpdateTime(object sender, EventArgs e)
        {
            timerText.Content = DateTime.Now.ToLongTimeString();
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);

            //Uncommnet below lines to stop memory leak
            timer.Tick -= UpdateTime;
            timer.Stop();
        }
    }
}

실행 후 위와 같은 방법을 이용해서 확인합니다.

이전과는 다르게 ClockWindow 50개가 표시되지 않는 것을 확인 할 수 있습니다.

간단한 셈플 프로그램과 Visual Studio의 Memory Usage 기능을 이용해서 메모리 누수 확인 하는 방법을 알아 보았습니다. Memory Usage에 대한 더 자세한 사항은 여기를 참고합니다.

메모리 누수가 심한 경우에는 스냅샷을 촬영하지 못하는 경우도 많이 발생하기 때문에 전문 툴인 dotMemory를 이용하는 것도 좋습니다.

셈플 코드

kaki104/WpfTest (github.com)

반응형
댓글