Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

(시리즈 글이 7개 있습니다.)
디버깅 기술: 68. windbg 분석 사례 - 메모리 부족
; https://www.sysnet.pe.kr/2/0/1837

디버깅 기술: 123. windbg - 닷넷 응용 프로그램의 메모리 누수 분석
; https://www.sysnet.pe.kr/2/0/11808

.NET Framework: 807. ClrMD를 이용해 메모리 덤프 파일로부터 특정 인스턴스를 참조하고 있는 소유자 확인
; https://www.sysnet.pe.kr/2/0/11809

.NET Framework: 944. C# - 인스턴스가 살아 있어 메모리 누수가 발생하고 있는지 확인하는 방법
; https://www.sysnet.pe.kr/2/0/12341

디버깅 기술: 171. windbg - 인스턴스가 살아 있어 메모리 누수가 발생하고 있는지 확인하는 방법
; https://www.sysnet.pe.kr/2/0/12342

.NET Framework: 945. C# - 닷넷 응용 프로그램에서 메모리 누수가 발생할 수 있는 패턴
; https://www.sysnet.pe.kr/2/0/12343

VS.NET IDE: 167. Visual Studio 디버깅 중 GC Heap 상태를 보여주는 "Show Diagnostic Tools" 메뉴 사용법
; https://www.sysnet.pe.kr/2/0/12699




C# - 인스턴스가 살아 있어 메모리 누수가 발생하고 있는지 확인하는 방법

아래와 같은 상황에서,

WPF - WindowsFormsHost를 담은 윈도우 생성 시 메모리 누수
; https://www.sysnet.pe.kr/2/0/12340

메모리 누수의 원인이... 의심이 가는 "그 인스턴스"가 살아 있어 발생하는 것인지 어떻게 확인할 수 있을까요? 그런 용도로 존재하는 것은 아니지만, WeakReference를 사용하면 그 목적을 이룰 수 있습니다.

예를 들어 위의 글에서 다룬 소스 코드의 MainWindow 측에 WeakReference를 이용해 약간의 코드를 더 추가해 보겠습니다.

using System;
using System.Collections.Generic;
using System.Windows;

namespace WpfApp5
{
    public partial class MainWindow : Window
    {
        System.Windows.Threading.DispatcherTimer _dispatcherTimer;
        Queue<WeakReference> _list = new Queue<WeakReference>();

        public MainWindow()
        {
            InitializeComponent();

            _dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
            _dispatcherTimer.Tick += _dispatcherTimer_Tick;
            _dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
            _dispatcherTimer.Start();
        }

        private void _dispatcherTimer_Tick(object sender, EventArgs e)
        {
            Queue<WeakReference> copyList = new Queue<WeakReference>();

            while (_list.Count > 0)
            {
                WeakReference wr = _list.Dequeue();
                if (wr.Target != null)
                {
                    copyList.Enqueue(wr);
                }
            }

            _list = copyList;
            System.Diagnostics.Trace.WriteLine(_list.Count);
            
            GC.Collect(2, GCCollectionMode.Forced);
        }

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

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var window = new Window1();
            window.Show();

            WeakReference wr = new WeakReference(window);
            _list.Enqueue(wr);
        }
    }
}

결과 비교를 위해 우선 메모리 누수가 발생하는 걸 확인해 봐야겠죠? 이를 위해 "Window1"의 OnClosed에서 WindowsFormsHost.Dispose 호출을 주석 처리한 후,

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }

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

Visual Studio에서 F5(Start Debugging) 키를 눌러 실행, "Button"을 누를 때마다 Output 창에 나오는 WeakReference.Target 속성 값이 살아 있는 수가 출력되고, 바로 그것이 쌓이면 메모리 누수가 되는 것입니다.

반면, 해당 주석을 다시 풀고 실행해 보면, 이제 GC.Collect로 인해 WindowsFormsHost 개체들이 정상적으로 GC 수집이 되므로 Button을 누를 때마다 Output 창에는 잠시 수가 늘었다가 0으로 내려가는 것을 볼 수 있습니다. 간단하죠?! ^^

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 9/23/2020]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... [91]  92  93  94  95  96  97  98  99  100  101  102  103  104  105  ...
NoWriterDateCnt.TitleFile(s)
11720정성태10/4/201825943개발 환경 구성: 405. Hyper-V 가상 머신에서 직렬 포트(Serial Port, COM Port) 사용
11719정성태10/4/201826654.NET Framework: 796. C# - 인증서를 윈도우에 설치하는 방법
11718정성태10/4/201821637개발 환경 구성: 404. (opkg가 설치된) Synology NAS(DS216+II)에 cmake 설치
11717정성태10/3/201823776사물인터넷: 48. 넷두이노의 C# 네트워크 프로그램 [1]
11716정성태10/3/201824914사물인터넷: 47. Raspberry PI Zero (W)에 FTDI 장치 연결 후 C/C++로 DTR 제어파일 다운로드1
11715정성태10/3/201822750사물인터넷: 46. Raspberry PI Zero (W)에 docker 설치
11714정성태10/2/201822862사물인터넷: 45. Raspberry PI에 ping을 hostname으로 하는 방법
11713정성태10/2/201823934개발 환경 구성: 403. Synology NAS(DS216+II)에 docker 설치 후 .NET Core 2.1 응용 프로그램 실행하는 방법
11712정성태10/2/201829491.NET Framework: 795. C# - 폰트 목록을 한글이 아닌 영문으로 구하는 방법 [3]
11711정성태10/2/201824769오류 유형: 490. 윈도우 라이선스 키 입력 오류 0xc004f050, 0xc004e028
11710정성태10/2/201824089.NET Framework: 794. C# - 같은 모양, 다른 값의 한글 자음을 비교하는 호환 분해 [5]
11709정성태9/30/201822807개발 환경 구성: 402. .NET Core 콘솔 응용 프로그램을 docker로 실행/디버깅하는 방법 [1]
11708정성태9/30/201825912개발 환경 구성: 401. .NET Core 콘솔 응용 프로그램을 배포(publish) 시 docker image 자동 생성 [2]파일 다운로드1
11707정성태9/30/201827230오류 유형: 489. ASP.NET Core를 docker에서 실행 시 "Failed with a critical error." 오류 발생 [1]
11706정성태9/29/201822066개발 환경 구성: 400. Synology NAS(DS216+II)에서 실행한 gcc의 Segmentation fault [2]
11705정성태9/29/201822657개발 환경 구성: 399. Synology NAS(DS216+II)에 gcc 컴파일러 설치
11704정성태9/29/201827985기타: 73. Synology NAS 신호음(beep) 끄기 [1]파일 다운로드1
11703정성태9/27/201821110개발 환경 구성: 398. Blazor 환경 구성 후 빌드 속도가 너무 느리다면? [2]
11702정성태9/26/201818968사물인터넷: 44. 넷두이노(Netduino)의 네트워크 설정 방법
11701정성태9/26/201824844개발 환경 구성: 397. 공유기를 일반 허브로 활용하는 방법파일 다운로드1
11700정성태9/21/201823948Graphics: 25. Unity - shader의 직교 투영(Orthographic projection) 행렬(UNITY_MATRIX_P)을 수작업으로 구성
11699정성태9/21/201822200오류 유형: 488. Add-AzureAccount 실행 시 "No subscriptions are associated with the logged in account in Azure Service Management (RDFE)." 오류
11698정성태9/21/201823613오류 유형: 487. 윈도우 성능 데이터를 원격 SQL에 저장하는 경우 "Call to SQLAllocConnect failed with %1." 오류 발생
11697정성태9/20/201820842Graphics: 24. Unity - unity_CameraWorldClipPlanes 내장 변수 의미
11696정성태9/19/201823172.NET Framework: 793. C# - REST API를 이용해 NuGet 저장소 제어파일 다운로드1
11695정성태9/19/201828425Graphics: 23. Unity - shader의 원근 투영(Perspective projection) 행렬(UNITY_MATRIX_P)을 수작업으로 구성
... [91]  92  93  94  95  96  97  98  99  100  101  102  103  104  105  ...