본문 바로가기
개발, IT

메모리 릭 찾기

by Nabi™ 2008. 6. 18.

CRT( C Runtime library )를 사용합시다.

아래의 코드를 사용하면 메모리 릭( 메모리 누수, Memory Leak )에 효과적으로 대처할 수 있다.
주의할 점은 사용법을 명확히 숙지하고서 사용할 것.

Introduce.

int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
    // Enable run-time memory check for debug builds.
#if defined(DEBUG) | defined(_DEBUG)
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif

다이렉트 X예제를 살펴보면 wWinMain시작 부분에 위와 같은 코드를 만날 수 있다.

이 코드는 디버그 시에 메모리릭이 발생하면 출력창에 몇 번지에서 얼마 크기의 메모리가 해제되지 않았는지를 표시 해 준다.

또한 메모리릭을 체크하기 위해서 <crtdbg.h>를 인클루드 해 줘야 하는데 DXUT.H를 인클루드 했다면 내부에서 <crtdbg.h>를 인클루드 해 주므로 따로 해 줄 필요가 없다.

아래에서 #define new 부분만 처리 해 주자.

아래에서 사용하는 코드들은

#if defined(DEBUG) | defined(_DEBUG)

로 감싸져 있기 때문에 릴리즈 모드에서는 동작하지 않는다. 또한 릴리즈 모드에서는 인클루드 자체가 제외 되기 때문에 효율을 걱정할필요가 없다.


Step 1.

#if defined(DEBUG) || defined(_DEBUG)
#include <crtdbg.h>
#define new new(_CLIENT_BLOCK, __FILE__, __LINE__)

_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

#endif


필요한 헤더를 인클루드 해 주고

#define new new(_CLIENT_BLOCK, __FILE__, __LINE__)

new자체를 새로 정의 해 버렸다. 메모리 릭 보고를 해 줄 때 릭을 발생시킨 녀석의 파일 이름과 라인을 출력 해 주어서 디버깅 시에 출력창에서 바로 찾아볼 수 있다. __FILE__과 __LINE__는 미리 정의된 매크로로 가끔 사용해 주면 유용하다.

_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

이 녀석이 실제적으로 출력창에 메모리 릭 보고를 해 준다. 5가지의 Flag가 있다.


_CRTDBG_ALLOC_MEM_DF

디폴트로 켜져 있으며, 디버그 버전에서 모든 메모리 할당이 일어날 때마다 추적 가능하도록 특별한 기능을 추가해 둔다. 이 플랙이 켜져 있어야 메모리 누수를 안전하게 검사 할 수 있다.
_CRTDBG_DELAY_FREE_MEM_DF

delete, free등으로 삭제되어도 바로 삭제되지 않고, CRT의 메모리 관리소에 남아 있다가 프로그램 종료시에 완전히 삭제된다.
_CRTDBG_CHECK_ALWAYS_DF

모든 메모리관련 연산에서 _CrtCheckMemory를 호출한다.
_CRTDBG_CHECK_CRT_DF

CRT가 내부적으로 할당한 블록도 메모리를 체크할 때 포함한다. 일반적으로는 CRT가 할당한 블록은 메모리 체크에서 제외된다. 일반적으로 사용하지 않는다
_CRTDBG_LEAK_CHECK_DF

프로그램이 완전히 종료되기 직전에 아직 해제되지 않은 메모리가 있는지 검사한다. 프로그램의 종료 포인트가 여러군데 있는 경우에 사용하면 일일이 _CrtDumpMemoryLeaks 메소드를 호출하지 않아도 자동적으로 메모리 누수를 검사할 수 있게된다.



Step 2.

메모리 할당이 시작되기 이전에 최상위에 만들어 두자.


Step 3.

Detected memory leaks!
Dumping objects ->
{840} normal block at 0x0109E460, 320 bytes long.
 Data: <  _             > 9C 07 5F 00 CD CD CD CD CD CD CD CD CD CD CD CD
{838} normal block at 0x0109E398, 46 bytes long.
 Data: <1 m   B o x   A > 31 00 6D 00 20 00 42 00 6F 00 78 00 20 00 41 00
{837} normal block at 0x0109E258, 260 bytes long.
 Data: <testAlpha.tga   > 74 65 73 74 41 6C 70 68 61 2E 74 67 61 00 CD CD
{836} normal block at 0x0109E218, 4 bytes long.
 Data: < e  > C0 65 1C 00
{835} normal block at 0x0109E198, 68 bytes long.
 Data: <   ?   ?   ?   ?> 00 00 80 3F 00 00 80 3F 00 00 80 3F 00 00 80 3F
{834} normal block at 0x0109E128, 48 bytes long.
 Data: <  _      SA `W  > A8 09 5F 00 98 E3 09 01 18 53 41 01 60 57 1C 00
Object dump complete.
D3DX: MEMORY LEAKS DETECTED: 3 allocations unfreed (708 bytes)
D3DX: Set HKLM\Software\Microsoft\Direct3D\D3DXBreakOnAllocId=0x21b to debug

 

고의적인 릭 발생이다.

#define로 new를 매크로로 씌워 놓은 것은 다이렉트에서 사용하는 메모리 체크 기능때문인지 제대로 안먹히는것 같아 보인다.

메모리릭은 첫번째 할당된 순서 그리고 주소 크기를 보여주고 메모리 안에 위치한 데이터를 보여준다.

DXUT를 사용하지 않은 프로젝트에서는 new를 매크로로 씌워 놓았을 때 출력창에서 릭이 발생한 파일과 줄 번호가 표시되어 이를 더블 클릭하면 그 위치를 직접 보여준다.

 

 

Step 4.

_CrtSetBreakAlloc( Number );

할당된 순서를 알 수 있다면 다음을 추가 시켜주자. 지정된 메모리가 할당될 때 디버깅을 중지하고 제어권을 디버거에게 넘긴다. 즉 어떤 놈이 할당을 시도하고 무책임하게 떠났는지 알 수 있다.

_CrtCheckMemory()

이 함수가 호출된 시점에서 지금까지 동적 할당된 모든 메모리를 체크해서 문제가 발생한 녀석이 있으면 0을 리턴한다.
0xFD : 메모리 블록 양 끝의 버퍼에 생성된다
0xCD : 새로 생성된 버퍼에 저장되는 기본값이다
0xCC : 스택에 변수가 생성되면 우선 이값으로 채워진다
0xDD : 삭제된 메모리 블록은 이 값으로 채워진다 

좋은정보가 되셨다면 아래 한번 클릭해주세요^^


댓글