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

비밀번호

댓글 작성자
 




... 76  77  78  [79]  80  81  82  83  84  85  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11992정성태7/22/201919703오류 유형: 560. 서비스 관리자 실행 시 "Windows was unable to open service control manager database on [...]. Error 5: Access is denied." 오류 발생
11991정성태7/18/201916723디버깅 기술: 128. windbg - x64 환경에서 닷넷 예외가 발생한 경우 인자를 확인할 수 없었던 사례
11990정성태7/18/201918655오류 유형: 559. Settings / Update & Security 화면 진입 시 프로그램 종료
11989정성태7/18/201917455Windows: 162. Windows Server 2019 빌드 17763부터 Alt + F4 입력시 곧바로 로그아웃하는 현상
11988정성태7/18/201920770개발 환경 구성: 453. 마이크로소프트가 지정한 모든 Root 인증서를 설치하는 방법
11987정성태7/17/201926687오류 유형: 558. 윈도우 - KMODE_EXCEPTION_NOT_HANDLED 블루스크린(BSOD) 문제 [1]
11986정성태7/17/201918343오류 유형: 557. 드라이브 문자를 할당하지 않은 파티션을 탐색기에서 드라이브 문자와 함께 보여주는 문제
11985정성태7/17/201918315개발 환경 구성: 452. msbuild - csproj에 환경 변수 조건 사용 [1]
11984정성태7/9/201926574개발 환경 구성: 451. Microsoft Edge (Chromium)을 대상으로 한 Selenium WebDriver 사용법 [1]
11983정성태7/8/201915767오류 유형: 556. nodemon - 'mocha' is not recognized as an internal or external command, operable program or batch file.
11982정성태7/8/201915800오류 유형: 555. Visual Studio 빌드 오류 - result: unexpected exception occured (-1002 - 0xfffffc16)
11981정성태7/7/201919248Math: 64. C# - 3층 구조의 신경망(분류)파일 다운로드1
11980정성태7/7/201928987개발 환경 구성: 450. Visual Studio Code의 Java 확장을 이용한 간단한 프로젝트 구축파일 다운로드1
11979정성태7/7/201919885개발 환경 구성: 449. TFS에서 gitlab/github등의 git 서버로 마이그레이션하는 방법
11978정성태7/6/201919165Windows: 161. 계정 정보가 동일하지 않은 PC 간의 인증을 수행하는 방법 [1]
11977정성태7/6/201923607오류 유형: 554. git push - error: RPC failed; HTTP 413 curl 22 The requested URL returned error: 413 Request Entity Too Large
11976정성태7/4/201918038오류 유형: 553. (잘못 인증 한 후) 원격 git repo 재인증 시 "remote: HTTP Basic: Access denied" 오류 발생
11975정성태7/4/201926583개발 환경 구성: 448. Visual Studio Code에서 콘솔 응용 프로그램 개발 시 "입력"받는 방법
11974정성태7/4/201922665Linux: 22. "Visual Studio Code + Remote Development"로 윈도우 환경에서 리눅스(CentOS 7) C/C++ 개발
11973정성태7/4/201921100Linux: 21. 리눅스에서 공유 라이브러리가 로드되지 않는다면?
11972정성태7/3/201925218.NET Framework: 847. JAVA와 .NET 간의 AES 암호화 연동 [1]파일 다운로드1
11971정성태7/3/201920884개발 환경 구성: 447. Visual Studio Code에서 OpenCvSharp 개발 환경 구성
11970정성태7/2/201920133오류 유형: 552. 웹 브라우저에서 파일 다운로드 후 "Running security scan"이 끝나지 않는 문제
11969정성태7/2/201920625Math: 63. C# - 3층 구조의 신경망파일 다운로드1
11968정성태7/1/201927339오류 유형: 551. Visual Studio Code에서 Remote-SSH 연결 시 "Opening Remote..." 단계에서 진행되지 않는 문제 [1]
11967정성태7/1/201921444개발 환경 구성: 446. Synology NAS를 Windows 10에서 iSCSI로 연결하는 방법
... 76  77  78  [79]  80  81  82  83  84  85  86  87  88  89  90  ...