상세 컨텐츠

본문 제목

c++ CRT 라이브러리를 사용하여 메모리 누수 찾기 관련

본문

반응형
728x170

 

 

=================================

=================================

=================================

 

 

 

출처: http://honestgame.tistory.com/101

 

 

 

 

 

VLD 라고 메모리 누수 체크용으로 사용하기 쉬운 라이브러리이다.

 

이름 그대로 VisualStudio 용으로 제작이 되었으며,

 

최신버전은 VS2015 도 지원된다.

 

 

사용방법은 매우 간단한데 설치된 폴더

 

C:\Program Files (x86)\Visual Leak Detector - 사용자 환경에 따라 다를 수 있음

 

의 lib와 include만 프로젝트 셋팅에 추가해 주면 된다.

 

 

 

VS가 이미 설치되어 있다면 install 할때 자동으로 경로가 추가 된다.

 

셋팅이 끝났다면,

 

해당 소스에 #include <vld.h> 만 추가해주면 끝.

 

 

 

 

프로그램이 끝날때 누수가 나는 곳을 체크해 준다.

 

참고로 이 기능을 추가했을 경우, 

 

프로젝트에 따라 디버깅이 상당히 느려질 수도 있다.

 

https://vld.codeplex.com/



 

vld-2.5-setup.exe
다운로드

 

 

 

 

 

 

 

=================================

=================================

=================================

 

 

출처: http://goguri.tistory.com/entry/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%88%84%EC%88%98-%EC%B2%B4%ED%81%AC-CrtSetDbgFlag-CrtDumpMemoryLeaks

 

1. 메모리 누스 체크 지원하는 헤더파일 추가한다.
   #include <crtdbg.h>
 
 
 
2. 코드 시작 부분에 메모리 누수 검출을 막기 위해 설정한다.
   _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); 
 
   - Flag 설정하지 않으면 상속구조에서 메모리 누수가 없어도 누수가 있다고 메시지 나온다.
   - STL의 경우에도 메모리 누수에 대한 잘못된 메시지가 나오게 된다.
 
 
 
3. 프로그램 종료시점에 해당하는 부분에 다음과 같이 입력한다.
   _CrtDumpMemoryLeaks();
 
 
 
4. 프로그램을 실행하면 메모리 누수가 발생할 경우 누수 발생에 대한 출력 메시지가 디버그 output 창에 나타난다.
 
 
 
-------------------------------------------------------------------
 
ex )
 
#include <crtdbg.h>
#include <stdlib.h>
#include <windows.h>
 
void main()
{
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); 
 
 
    ..... ( 요약 ) .....
 
 
    while( WM_QUIT != msg.message )
    {
        if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        else
        {
            if( CheckFrame() )
   {
cGameFrame->Update();
cGameFrame->Draw();
   }
        }
    }
 
    _CrtDumpMemoryLeaks();
  
}
 
 
==================================================
 
=> 실행시 메모리 누수가 체크되면 디버그 output창에서 메모리 누수 결과 메시지를 표시해 준다.
 
ex )
   Detected memory leaks!
   Dumping objects ->
   {236333} nomal block at 0x02BC31D8, 8 bytes long.
   Data: <   > 08 00 00 00 00 00 00 00
   {236331} nomal block at 0x02B808A0, 8 bytes long.
   Data: <   > 08 00 00 00 09 00 00 00
 
   ..... ( 요약 ) .....
 
 
-------------------------------------------------------------------
 
 
출처: 
 
 
 

 

=================================

=================================

=================================

 

 

 

출처: http://freelyblog.tistory.com/200

 

 

메모리 누수 체크

 

C#이나 Java 같은 언어를 사용하는 사람은 메모리 해제에 대해 전혀 신경쓸 필요가 없다. 가비지 컬렉터라는 자동 메모리 관리 기능을 언어 차원에서 지원해주기 때문이다.

 

하지만 C++은 그렇지 않다. 메모리의 할당도, 할당한 메모리의 해제도, 모두 사용자의 몫이다. 때문에 동적 할당으로 생성된 메모리를 적절한 타이밍에 해제하지 않으면 해당 메모리는 생성된 만큼의 공간을 차지하면서도 정작 사용할 수 없는 상태가 된다. 바로 메모리 누수 현상이다.

 

그저 new 키워드를 이용해 메모리를 할당한 포인터에 delete 키워드를 적용 시키면 끝나는 얘기지만, 어찌됐든 사용자는 인간이다. 말 그대로 깜박 잊을 수도 있고, 포인터 사이를 이리저리 날아다니는 메모리를 제대로 추적하지 못해 해제하는 타이밍을 놓칠 수도 있다.

 

메모리 누수 현상을 막는 데는 여러가지 방법이 있지만 그건 다음 포스트에서 알아보기로 하고, 이번 포스트에서는 메모리 누수의 발생 여부를 검사하는 방법을 알아보고자 한다.

 

#include <iostream>

 

using namespace std;

 

int plus(int A, int B) {

int* a;

int* b;

 

a = new int(A);

b = new int(B);

 

return *a + *b;

}

 

void main() {

cout << plus(10,20) << endl;

 

위 소스에 있는 plus 라는 함수는 int형 매개 변수 2개를 받고 있으며, 내부적으로는 int형 포인터 변수를 2개 선언한 후에 거기에 new 키워드를 이용해 메모리를 할당하면서 각각 A와 B 값으로 초기화하고 두 변수의 값을 더해 돌려주고 있다.

 

보면 알겠지만, plus 함수에서는 new 키워드는 사용됐으나, delete 키워드는 눈을 씻고 찾아봐도 없다. 거기다 포인터를 반환하는 것도 아니기 때문에 함수가 종료되면 사용자는 더이상 손댈 수 없게 된다. 즉, 이 함수를 사용할 때마다 8Byte (int * 2)가 허공으로 사라진다.

 

이런 단순한 경우는 쉽게 예측이 가능하지만, 조금이라도 소스가 복잡해진다면 메모리 누수가 진행 중인지 아닌 지조차 알 수 없게 되는 경우도 생긴다. 그럴 때는 아래의 방법을 사용하면 된다.

 

#include <iostream>

#include <crtdbg.h>

 

using namespace std;

 

#ifndef _DEBUG

#define new new(_CLIENT_BLOCK,__FILE__,__LINE)

#endif

 

int plus(int A, int B) {

int* a;

int* b;

 

a = new int(A);

b = new int(B);

 

return *a + *b;

}

 

void main() {

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

 

cout << plus(10,20) << endl;

 

1. 

 

crtdbg.h 를 include 한다.

 

2.

 

#ifndef _DEBUG

#define new new(_CLIENT_BLOCK,__FILE__,__LINE)

#endif

 

위 3문장을 헤더파일에 작성한다.

여기서는 알아보기 쉽게 main.cpp 에 작성했다.

 

3.

 

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);

 

main() 함수의 제일 첫부분에 위 문장을 작성한다.

메모리 누수 발생이 의심되는 부분에 작성해도 되긴 하지만 그냥 메인 함수 꼭대기에 써놓는 게 편하다.

 

4.

 

디버깅을 시작한다.

 

 

 

 

4 Bytes * 2 의 메모리가 누수됐다고 출력창에 나온다.

 

 

 

 

 

delete 키워드를 추가하면 메모리 누수가 없어졌음을 알 수 있다.

 

이 방법을 사용하면 디버그 할 때 메모리 누수 발생 여부를 알 수 있지만 어디서 메모리 누수가 발생했는 지는 알 수 없다. 행여 메모리가 포인터 사이를 이리저리 돌아다니는 소스에서 메모리 누수가 발생했다면 문제가 심각해지므로 메모리 누수는 미연에 방지하는 것이 제일 좋다.

 

 

 

 

반응형

 

728x90

 

 

 

=================================

=================================

=================================

 

 

출처: http://kuaaan.tistory.com/141

 

 

디버그 환경에서 재현되는 메모리 릭의 경우 가장 편리하게 디버깅하는 방법은 CRT Debug 함수를 사용하는 것입니다.
_CrtSetDbgFlag() API를 사용하면 VS에서 Debug 실행 종료시 DebugString으로 Leak 내용이 출력됩니다. (MFC의 경우 디폴트로 세팅됨) 
코드 시작부분에 다음과 같이 선언해주면 함수 종료시 메모리 릭 여부를 체크하여 릭이 있을 경우 아래과 같이 DebugString으로 출력해 줍니다.

  1. #include <crtdbg.h>  
  2.   
  3. int _tmain(int argc, _TCHAR* argv[])  
  4. {  
  5.     _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );  
  6.     // 어쩌구 저쩌구...  
  7.     return TRUE;  
  8. }  



Detected memory leaks!

Dumping objects ->
c:\program files\microsoft visual studio 8\vc\include\crtdbg.h(1150) : {50} normal block at 0x003A2F50, 100 bytes long.
Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.
The program '[3868] MemoryLeakTest.exe: Native' has exited with code 0 (0x0).


메시지 중 "{50}"이라는 숫자가 있는데 MSDN에서는 이걸 "Allocation order number"라고 소개하고 있습니다. 말하자면, 50번째로 할당된 메모리에서 Leak이 발생했다는 의미이죠.

이때  _CrtSetBreakAlloc() 함수에 저 번호를 넘겨주면 해당 메모리가 할당된 지점에서 BreakPoint가 걸립니다.

  1. #include <crtdbg.h>  
  2.   
  3. int _tmain(int argc, _TCHAR* argv[])  
  4. {  
  5.     _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );  
  6.     _CrtSetBreakAlloc(50);       // 여기!!  
  7.     // 어쩌구 저쩌구...  
  8.     return TRUE;  
  9. }  



 


이제 VisualStudio의 CallStack 창에서 해당 메모리가 어디서 할당되었는지 확인만 하시면 되시겠습니다.
단, 위의 함수들은 Debug Only!! ^^ 만약 릴리즈환경에서 오랫동안 실행되어야 확인되는 오류라면 LeakDiag 등 메모리누수 탐지 도구를 사용해야 합니다. 또, 멀티스레드라던가 해서 여러번 실행했을 때 메모리 할당순서가 매번 바뀌는 경우라면 이 방법(CRT Debug함수)은 소용 없을 듯 합니다. 메모리가 할당된 번호를 기준으로 BP를 거니까요.. 

자세한 내용은 여기에...

http://again4you.egloos.com/1350482


아래 글에서는 CRT 디버그함수를 이용해 메모리누수 뿐만 아니라 delete된 메모리를 재사용하는 오류, 배열의 경계를 침범하는 오류 등을 체크하는 방법도 소개되어 있네요. ^^

http://challengeok.tistory.com/entry/%ED%8E%8C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%88%84%EC%88%98-%EB%94%94%EB%B2%84%EA%B9%85

(가급적이면 원본 글을 찾아서 링크하려고 했는데... 원본은 없어진 것 같고 퍼온글들만 10여건이 검색되네요. ^^;;)

좋은 내용 공유해주신 again4you 님과 gazette2 님께 감사! ^^

 

 

 

=================================

=================================

=================================

 

 

 
 
출처: http://egloos.zum.com/sungod0/v/3561771

MFC를 사용하여 프로그램을 개발하고 있다면,

#ifdef _DEBUG
#define new
 DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


위의 코드를 .cpp 화일에 넣어 프로그램이 종료 되었을때 아래와 같이 누수된 메모리를 할당하는 부분의 소스 코드와 라인 수를 출력 해준다.

Detected memory leaks!
Dumping objects ->
d:\sample\sample.cpp(35) : {48} client block at 0x003739D0, subtype 0, 4 bytes long.
 Data: <(   > 28 00 00 00 
d:\sample\sample.cpp(34) : {47} client block at 0x00373990, subtype 0, 4 bytes long.
 Data: <    > 1E 00 00 00 
Object dump complete.


위의 예에서는 두 블록의 메모리 누수가 검출 되었는데 각 라인의 의미는 

d:\sample\sample.cp (35) : {48} client block at 0x003739D0, subtype 0, 4 bytes long.


sample.cpp 화일의 35번째 라인에서 할당된 메모리가 누수되었고 그것은 48번째로 할당된 메모리이며 메모리 주소는 0x003739d04Byte가 누수되었다는 의미로 해석할 수 있다.

MFC를 사용하지 않은 프로그램의 경우 위의 DEFINE 문으로는 DEBUG_NEW를 찾을 수 없다는 컴파일 에러를 내뱉는데 그 경우엔 위의 DEFINE 문과 함께 아래와 같은 라인을 헤더에 삽입해서 해결할 수 있다.

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

DEBUG_NEW를 새로이 정의함으로써 DEBUG_NEW를 찾을 수 없는 문제를 해결한다.

간혹 malloc 을 이용한 메모리 할당은 위의 내용이 적용이 되질 않는데 이 경우엔 아래의 문장을 소스코드에 삽입한다.

- 헤더 화일에는 아래라인을 추가
#define DEBUG_MALLOC(size) _malloc_dbg(size, _NORMAL_BLOCK, __FILE__ , __LINE__) 

- malloc을 재정의 하려는 소스 코드에 아래 라인을 추가.
#define malloc DEBUG_MALLOC

위의 방법들로 memory leak dump 에서 할당된 소스 코드와 라인수를  바로 찾을 수 있다.

--------------------------------------------------------------------------------------------------------------------
MFC로 프로젝트 만들어서 디버그하면 프로세스가 종료될 때 메모리 릭을 체크해서 출력창에 보여주는데, 일반 콘솔 어플리케이션이나 WIN32 어플리케이션 프로젝트로 작업하면 그런 정보를 알려주지 않아서 가끔 아쉬울 때가 있다.

그런데.. 이 '메모리 릭 검출 기능'이 MFC 프로젝트에서만 가능한 게 아니라 함수 하나만 호출해 주면 다른 프로젝트에서도 가능하다는 걸 알았다.
(이걸 이제서야 알게 되다니...OTL)

그냥 간단하게,

#include <crtdbg.h>

_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );


이렇게만 추가해 주면 된다.
(함수 호출은 main이나 WinMain함수 안에서...)


덧글|덧글 쓰기

 
 

 

=================================

=================================

=================================

 

 

 
 
 
 
 
 
 

 

 

 

 

 

 

 

반응형
그리드형


관련글 더보기

댓글 영역