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

비밀번호

댓글 작성자
 




... 61  62  63  64  65  66  67  68  69  70  71  [72]  73  74  75  ...
NoWriterDateCnt.TitleFile(s)
12167정성태3/5/202019873개발 환경 구성: 471. C# - 닷넷 응용 프로그램에서 DB2 Express-C 데이터베이스 사용 (3) - ibmcom/db2express-c 컨테이너 사용
12166정성태3/4/202020950개발 환경 구성: 470. Windows Server 컨테이너 - DockerMsftProvider 모듈을 이용한 docker 설치
12165정성태3/2/202019354.NET Framework: 900. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 네 번째 이야기(Monitor.Enter 후킹)파일 다운로드1
12164정성태2/29/202020504오류 유형: 598. Surface Pro 6 - Windows Hello Face Software Device가 인식이 안 되는 문제
12163정성태2/27/202018810.NET Framework: 899. 익명 함수를 가리키는 delegate 필드에 대한 직렬화 문제
12162정성태2/26/202023317디버깅 기술: 166. C#에서 만든 COM 객체를 C/C++로 P/Invoke Interop 시 메모리 누수(Memory Leak) 발생 [6]파일 다운로드2
12161정성태2/26/202019133오류 유형: 597. manifest - The value "x64" of attribute "processorArchitecture" in element "assemblyIdentity" is invalid.
12160정성태2/26/202019415개발 환경 구성: 469. Reg-free COM 개체 사용을 위한 manifest 파일 생성 도구 - COMRegFreeManifest
12159정성태2/26/202015962오류 유형: 596. Visual Studio - The project needs to include ATL support
12158정성태2/25/202019176디버깅 기술: 165. C# - Marshal.GetIUnknownForObject/GetIDispatchForObject 사용 시 메모리 누수(Memory Leak) 발생파일 다운로드1
12157정성태2/25/202018645디버깅 기술: 164. C# - Marshal.GetNativeVariantForObject 사용 시 메모리 누수(Memory Leak) 발생 및 해결 방법파일 다운로드1
12156정성태2/25/202017177오류 유형: 595. LINK : warning LNK4098: defaultlib 'nafxcw.lib' conflicts with use of other libs; use /NODEFAULTLIB:library
12155정성태2/25/202017084오류 유형: 594. Warning NU1701 - This package may not be fully compatible with your project
12154정성태2/25/202016298오류 유형: 593. warning LNK4070: /OUT:... directive in .EXP differs from output filename
12153정성태2/23/202020766.NET Framework: 898. Trampoline을 이용한 후킹의 한계파일 다운로드1
12152정성태2/23/202019190.NET Framework: 897. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 세 번째 이야기(Trampoline 후킹)파일 다운로드1
12151정성태2/22/202020542.NET Framework: 896. C# - Win32 API를 Trampoline 기법을 이용해 C# 메서드로 가로채는 방법 - 두 번째 이야기 (원본 함수 호출)파일 다운로드1
12150정성태2/21/202020915.NET Framework: 895. C# - Win32 API를 Trampoline 기법을 이용해 C# 메서드로 가로채는 방법 [1]파일 다운로드1
12149정성태2/20/202019004.NET Framework: 894. eBEST C# XingAPI 래퍼 - 연속 조회 처리 방법 [1]
12148정성태2/19/202022263디버깅 기술: 163. x64 환경에서 구현하는 다양한 Trampoline 기법 [1]
12147정성태2/19/202019212디버깅 기술: 162. x86/x64의 기계어 코드 최대 길이
12146정성태2/18/202020102.NET Framework: 893. eBEST C# XingAPI 래퍼 - 로그인 처리파일 다운로드1
12145정성태2/18/202020572.NET Framework: 892. eBEST C# XingAPI 래퍼 - Sqlite 지원 추가파일 다운로드1
12144정성태2/13/202020761.NET Framework: 891. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 두 번째 이야기파일 다운로드1
12143정성태2/13/202016585.NET Framework: 890. 상황별 GetFunctionPointer 반환값 정리 - x64파일 다운로드1
12142정성태2/12/202019538.NET Framework: 889. C# 코드로 접근하는 MethodDesc, MethodTable파일 다운로드1
... 61  62  63  64  65  66  67  68  69  70  71  [72]  73  74  75  ...