Development.

[DEV] CRT 라이브러리 사용하여 메모리누수 찾기

Chuuu_DevCamp:) 2023. 8. 29. 20:10
반응형

1) 개요

메모리 누수 문제는 C++앱에서 가장 감지하기 어려운 이슈 중 하나로, 이전에 할당했던 메모리를 해제하지 않으면 메모리 누수가 발생한다.
이러한 문제를 감지하기 위해 CRT 라이브러리에 대해 알아보았다.

메모리 누수 감지 기능을 사용하기 위해서 다음과 같이 선언되어야 한다.메모리 누수 문제는 C++앱에서 가장 감지하기 어려운 이슈 중 하나로, 이전에 할당했던 메모리를 해제하지 않으면 메모리 누수가 발생한다.
이러한 문제를 감지하기 위해 CRT 라이브러리에 대해 알아보았다.

메모리 누수 감지 기능을 사용하기 위해서 다음과 같이 선언되어야 한다.

#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>

<crtdbg.h>을 포함하면 malloc과 free가 디버그 버전 _malloc_dbg, _free_dbg에 매핑된다. 이 매핑은 debug 빌드일때만 발생하고, 릴리즈 빌드에서는 일반적인 malloc과 free가 사용된다.

_CRTDBG_MAP_ALLOC을 포함하지 않는다면, 앱이 종료될 때 메모리 누수 보고서를 자세하게 표시할 수 없다.

2) 사용 방법

위와 같이 <crtdbg.h>와 _CRTDBG_MAP_ALLOC을 선언하였다면, 아래의 함수들을 사용할 수 있다. 아래 관련한 함수들에 대해 소개하고 사용 방법을 설명하겠다.

_CrtDumpMemoryLeaks();
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_DEBUG );
_CrtSetBreakAlloc();

_CrtDumpMemoryLeaks()

앱 종료 지점 앞에 호출해서 메모리 누수 보고서를 표시할 수 있다. 

/* _CrtDumpMemoryLeaks() 사용 예시 */
#define _CRTDBG_MAP_ALLOC
#include "pch.h"
#include <crtdbg.h>

int main() {
    int* ptr = new int[10];
    for (int idx = 0; idx < 10; idx++) {
        ptr[idx] = idx + 1;
    }

    _CrtDumpMemoryLeaks();
    return 0;
}

위 코드의 앱을 실행 및 종료하면 출력창에서 메모리 누수 보고서를 확인 할 수 있다.
동적할당한 메모리를 해제하지 않고 종료하자 출력창에 메모리 누수에 관련한 내용을 출력해주고 있다.

해당 함수는 프로그램이 종료되는 지점에 사용하는데, 첫 누수 결과만 출력해준다. 만약 확인 해보고 싶은 부분이 여러곳이라면, 확인 해보고싶은 모든 부분에 해당 함수를 호출해 주어야 하는 번거로움이 있다. 

_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF )

위 함수는 _CrtDumpMemoryLeaks() 함수와 달리 프로그램의 시작 부분에 한번 사용하면 탐지 가능한 모든 누수를 결과창에 표시한다.

해당 함수 파라미터로 전달하는 bit flag의 종류는 여러 가지가 있으니 필요할 경우 찾아보고 사용하길 바란다.

/* _CrtSetDbgFlag() 사용 예시 */
#define _CRTDBG_MAP_ALLOC
#include "pch.h"
#include <crtdbg.h>

int main() {
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    int* ptr = new int[10];
    for (int idx = 0; idx < 10; idx++) {
        ptr[idx] = idx + 1;
    }
    return 0;
}

_CrtDumpMemoryLeaks()함수와 동일한 결과를 출력하는것을 알 수 있다.

_CrtSetReportMode( _CRT_WARN, _CRTDBG_MODE_DEBUG )

기본적으로 _CrtDumpMemoryLeak()는 출력창-디버그에서 메모리 누수 보고서를 출력한다. 만약 _CrtSetReportMode 함수로 보고서를 다른 위치로 리디렉션 할 수 있다.

이 함수는 사용하지 않아도 기본으로 출력창엔 출력되기 때문에 추가로 설정하고 싶다면 다음 링크를 참고하자.

_CrtSetReportMode | Microsoft Learn

 

_CrtSetReportMode

자세한 정보: _CrtSetReportMode

learn.microsoft.com

_CrtSetBreakAlloc() / _crtBreakAlloc

_CrtSetBreakAlloc()은 함수이고 _crtBreakAlloc은 매크로인데, 둘 다 동일한 기능을 하여 함께 소개하겠다.

누수 발생 시 누수 지점의 번호가 출력되는데 그 번호를 파라미터로 전달하면 그 지점에서 프로그램이 멈춘다.

위 출력 창에서 예를 들자면, 빨간색 동그라미에 누수지점 번호를 확인 할 수 있다. 이 번호를 _CrtSetBreakAlloc() 함수의 파라미터로 전달하면 메모리 누수 지점에서 프로그램이 멈춘다.

메모리 누수 보고서를 좀더 자세히 작성하도록 하는 방법

위의 결과에서 볼 수 있듯이 어느 부분에서 누수가 발생하게 됐는지 한눈에 알기가 어렵다. 따라서, 누수가 발생하는 파일과 라인 번호까지 출력해주는 매크로와 사용 방법에 대해 작성하였다.

new 연산자에 인자를 추가해서 DBG_NEW라는 매크로를 만들어서 사용하면 된다. 아래와 같이 Debug 모드에서는 인자를 추가한 custom new를 사용 하고 Release 모드에서는 일반 new를 사용하도록 정의한다.

위에서 말한 것과 같이 DBG_NEW 매크로를 사용해서 테스트한 결과, 다음과 같이 메모리 누수가 발생한 파일과 라인을 알 수 있었다.

/* DBG_NEW 매크로 사용 예시 */
#define _CRTDBG_MAP_ALLOC
#include "pch.h"
#include <crtdbg.h>

#ifdef _DEBUG
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#else
#define DBG_NEW new
#endif

int main() {
    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    int* ptr = DBG_NEW int[10];
    for (int idx = 0; idx < 10; idx++) {
        ptr[idx] = idx + 1;
    }
    return 0;
}

메모리 누수 보고서 해석 방법

위와 같이 메모리 누수 보고서가 출력되었다면 이를 어떻게 해석해야 할 지 정리했다.

{157} → 할당된 메모리 번호

normal block → 메모리 영역의 블럭 형식.
normal / client / CRT로 나뉘어지며, normal 은 new연산자로 할당된 메모리를 나타낸다. client는 MFC 관련, CRT는 CRT 라이브러리 자체 용도에 맞게 할당된 블럭

0x0094ABE8 → 누수가 일어난 메모리 주소

Data: <  > 01 00 00 00 02 00 00 00 ....

→ 해당 주소의 첫 16바이트 값을 16진수로 나타낸 것. 

'Development.' 카테고리의 다른 글

[DEV] Google Test  (0) 2023.08.28
[DEV] 신인 프로그래머가 알아야 할 프로그래밍 6선  (1) 2020.08.17