Jump to content
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr ×
과거의 기술자료(읽기 전용): https://tech.devgear.co.kr

[튜토리얼 번역] 딜리커 (Deleaker)를 사용하여 "C++빌더" 에서 메모리 누수를 찾는 방법


Recommended Posts

Deleaker 도움말 중 C++빌더에서 사용하는 방법을 번역했습니다.

도입

이 튜토리얼은 C++빌더 안에서 바로 메모리 누수를 감지하는 방법을 설명한다. 또한 누수를 일으키는 소스 사이를 이동하는 방법과 누수를 일으키는 스냅샷을 이용하여 어떻게 소스를 찾는 지를 설명한다. 

RAII, 스마트 포인터 등 현대식 기술들을 사용함에도 불구하고, 메모리 누수는 여전히 발생될 수 있다. malloccallocnewdelete 등 메모리 할당을 직접하는 코드를 사용하지 않더라도 마찬가지이다. 예를 들어, 스마트 포인터의 순환 참조는 누수를 발생시킨다. 오래된 프로젝트의 오래된 코드는 현대식 C++로 다시 작성할 수 없는 것이 일반적이며 이와 동시에 메모리 누수가 발생하므로 즉시 수정해야한다. 

설치

딜리커(Deleaker)는 독립형 메모리 프로파일러로 실행할 수도 있고, C++빌더에서 플러그인으로 실행할 수도 있다.

누수가 고객의 장비에서는 재현이 되지만, 개발자의 장비에서는 재현되지 않는 경우가 있는데, 독립형 Deleaker는 이런 경우에 사용한다.

개발을 진행하는 동안 매일 사용하려면 Deleaker를 C++빌더에 플러그인한다. Deleaker가 일단 C++빌더에 플러그인으로 설치되고 나면, 개발자는 C++빌더 안에서 메모리 스냅샷을 찍고, 디버깅하면서 메모리 누수를 바로 찾을 수 있다.

Deleaker는 C++빌더 2010 버전과 그 이후의 모든 버전에 플러그인 될 수 있다. 만약 C++빌더 6.0 버전을 사용하고 있다면 독립형  Deleaker를 사용하여 메모리 누수를 파악하기 바란다.

Deleaker는 C++빌더에 설치될 때 , 컴퓨터에 이미 설치되어 있는 C++빌더 버전 중에 플러그인 될 수 있는 버전을 인식하고, 해당 버전에 플러그인 된다.

Deleaker를 설치하고 나서 RAD 스튜디오를 실행하면, 메인 메뉴에 Deleaker 항목이 새로 추가된 것을 볼 수 있다. Deleaker를 열려면, 메인 메뉴 > Deleaker > Deleaker Window를 클릭한다. Deleaker 활성화가 디펄트 설정이므로, 메모리 누수를 바로 프로파일링할 수 있다. Deleaker를 비활성화/활성화 하려면 Deleaker > Enabled 를 사용한다.
1_deleaker_menu.gif
그림1. C++빌더에 들어간 Deleaker 메뉴

메모리 누수 추가

폼 하나에 버튼 하나만 있는 간단한 윈도우 VCL 애플리케이션을 만든다. 이 버튼의 OnClick 이벤트에 다음 코드를 사용한다.

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    int* p = new int[1024];
}

프로젝트 설정

Deleaker는 (무엇을 이용하여 어떻게 만들었는 지와 관계없이) 어떠한 애플리케이션도 프로파일링 할 수 있지만, 보다 많은 것을 포함하는 결과를 얻으려면 프로젝트 옵션을 설정하는 것이 더 좋다.

디버그 정보

디버그 정보를 생성하도록 지정하는 것이 가장 중요하다. 디버그 정보는 Deleaker가 주소를 해석하여 (예: 해당 주소에 상응하는 소스 파일 경로와 줄 번호) 메모리 누수가 발생되는 위치를 개발자가 손쉽게 이해할 수 있도록 한다.

디버그 정보 생성을 활성화하려면, 메인 메뉴 > Project > Options...를 클릭하면 표시되는 프로젝트 옵션 화면에서, Building > C++ Compiler > Debugging 항목을 선택한 후 Debug information 과 Debug line number information 모두 True를 선택한다.

Enable Codeguard는 False를 선택하는 것이 좋다.
2_project_options_debug_information.png
그림2. C++빌더에서 컴파일러가 디버그 정보를 생성하도록 설정하기

링커에서도 전체 디버그 정보를 생성하도록 하려면 Building > C++ Linker 항목을 선택한 후 Full debug information에서 True를 선택한다.
3_linker_debug_information.png
그림3. C++빌더에서 링커가 전체 디버그 정보를 생성하도록 설정하기

만약 명령줄을 사용하여 프로젝트를 빌드하는 경우,  -v-y 옵션을 사용한다.

스택 ( Stack ) 프레임 생성

메모리가 할당될 때, Deleaker 고리는 콜 스택을 저장한다. Deleaker가 모든 호출자에 대한 정보를 얻으려면 스택 프레임이 필요하다. 스택 프레임 생성을 활성화하려면, 메인 메뉴 > Project > Options를 클릭하면 표시되는 프로젝트 옵션 화면에서, Building > C++ Compiler 를 선택한 후 General Compilation > Standard stack frames 항목에서 True를 선택한다.
4_enable_stack_frames.png
그림4. C++빌더에서 스택 프레임을 활성화하기

만약 명령줄을 사용하여 프로젝트를 빌드하는 경우,  -k 옵션을 사용한다.

최적화 비활성화하기

Deleaker는 어떤 실행파일에서도 메모리 누수를 탐지할 수 있다. 빌드 구성이 어떻든지 관계없이 탐지하지만, 메모리 할당과 관련된 보다 정확하고 명확한 정보를 얻으려면 모든 최적화 옵션을 비활성화하는 것이 더 좋다. 메인 메뉴 > Project > Options...를 클릭하면 표시되는 프로젝트 옵션 화면에서, Building > C++ Compiler > Optimizations 항목을 선택한 후 Disable all optimizations 항목에서 True를 선택한다.
5_disable_optimizations.png
그림5. C++빌더에서 최적화 비활성화하기

메모리 누수 파악하기

Deleaker가 메모리 누수에 대하여 최대한 많은 정보를 수집할 수 있도록 프로젝트 옵션을 설정하고나면, 빌드하고 실행한다. 앞에서 만든 폼 하나에 버튼 하나가 있는 애플리케이션이 나타나면 버튼을 클릭하여 메모리 누수를 만들고 나서 폼을 닫아서 종료한다. Deleaker가 활성화되어 있다면, 해당 폼을 실행하던 프로세스가 나갈 때  Deleaker는 스냅샷을 찍는다. 여기에는 해제되지 않은 모든 메모리 할당이 들어가는데, 이런 할당이 소위 메모리 누수이다. 앞에서 일부러 만든 누수의 결과는 아래 그림과 같다.
6_taking_leaks.gif
그림6. 스냅샷 찍기와 그 결과

누수를 일으키는 코드로 이동하려면 할당 항목을 더블클릭하거나 콜 스택 항목 중 하나에서 오른쪽 클릭을 하고 Show Source Code를 선택한다. C++ Builder에 해당되는 소스 파일이 열리고 누수를 일으키는 곳의 코드가 보인다.
7_navigate_source_code.gif
그림7. 누수를 일으키는 소스 코드로 이동하기

영속하는 메모리 누수 수정하기

프로세스가 사용하는 메모리가 점점 더 많아진다는 것을 알게 되는 경우가 때때로 있다. 코드 중에 메모리를 가져오는 특정 부분이 계속해서 호출되고 있을 수 있다. 이런 동작이 의도한 바 일 수도 있고 아닐 수도 있지만, 어떤 경우라도 개발자는 이런 코드의 위치를 찾아내야 한다.

Deleaker 창에서 바로 메모리 사용을 볼 수 있다는 점을 알아두면 좋다. 디버깅 중에 Resource Usage Graph로 가면 아래와 같은 화면이 보인다.
8_memory_usage_graph.gif
그림8. Deleaker의 리소스 사용 그래프

영속적인 메모리 누수를 만드는 코드를 추가해보자. 폼에 타이머를 하나 올리고, 이것을 활성화한다. Interval을 500ms로 설정하고 이 타이머의 OnTimer 이벤트에서 메모리 할당을 하자.

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
    int* p = new int[1000000];
}

프로젝트를 빌드하고 실행한다. 해당 프로세스가 실행되도록 그대로 둔 채로, Deleaker로 와서 Take Snapshot을 클릭한다. 현재 할당된 메모리와 기타 리소스가 보일 것이다.  Hit Count 열을 보자. 여기에는 동일한 위치에 동일한 호출자가 만든 할당의 갯수가 표시된다. 따라서 동일한 곳에서 매우 많이 할당이 발생하는 리소스가 무엇인지, 그리고 Hit Count의 숫자를 통해 그 횟수가 얼마인지를 쉽게 식별할 수 있다. 즉 Hit Count는 영속적인 메모리 누수를 수정할 때 도움이 된다.
9_hit_count.gif
그림9. 스냅샷 찍기와 Hit Count 보기

이렇게 꾸준하게 누수되는 리소스를 탐지하는 또 다른 방법이 있다. 스냅샷 두개를 찍고 서로 비교하면, 각 스냅샷이 찍힌 시점들 사이에 발생한 메모리 할당만 볼 수 있다 (예: 꾸준하게 발생한 메모리 할당 축적). 스냅샷을 비교하려면 첫번째 스냅샷을 선택하고, 그 옆에 있는 Compare with...를 클릭하여 두번째 스냅샷을 선택한다. 그러면 Deleaker가 이 두개의 스냅샷 사이에 달라진 것만 계산한다.
10_compare_snapshots.gif
그림10. 스냅샷 비교하기

VCL 오브젝트 누수

C++빌더 코드는 거의 항상 VCL 오브젝트를 생성하고 사용한다. 개발자는 자신이 직접 생성한 오브젝트를 명시적으로 제거해야 하는데 깜빡하고 놓치기 쉽다. C++빌더로 개발한다면 이런 오브젝트 누수가 아마도 가장 흔할 것이다. 

Deleaker가 이런 오브젝트 목록을 제공하는 이유가 바로 이것 때문이다. 이런 오브젝트를 살펴보려면 Delphi Objects 탭으로 이동한다.

오브젝트는 클래스 별로 그룹핑되어 있다. 각 클래스 별로 해당 오브젝트가 목록으로 표시되고, 각 오브젝트 별로 콜 스택이 표시된다. 각 클래스에는 생성된 오브젝트의 갯수와 전체 크기가 표시되므로, 가장 큰 것을 찾을 수 있다. 특정 클래스에 해당되는 오브젝트를 빠르게 찾으려면 Filter by name을 사용한다.  클래스 이름을 타이핑하기 시작하면 해당되는 클래스만 표시된다.
11_vcl_objects.gif
그림11. 살아있는 VCL 오브젝트 잡기와 클래스 이름으로 델파이 클래스 필터링하기

결론

메모리 누수 탐지는 간단한 작업이 아니다. 코드가 아무리 깔끔하고 개발자가 현대식 C++ 표준을 잘 따르는 경우에도 마찬가지이다. C++빌더로 작성된 코드는 VCL 오브젝트를 사용하는 경향이 많은데 이 VCL 오브젝트들은 전통적으로 할당과 해제 모두 명시적이어야 한다. 실수로 누수가 발생하기 쉽다.

이런 이유 때문에 Deleaker와 같은 메모리 프로파일러는 요즘 모든 개발자들에게 매우 중요한 도구이다. 

Deleaker는 C++빌더 안에 완전히 통합된다. 따라서 메모리, VCL 오브젝트, GDI 리소스 등의 할당을 편안하게 탐색할 수 있다. Deleaker는 범용 메모리 누수 탐지 도구이므로 윈32, 윈64 애플리케이션 모두를 대상으로 할 수 있고 전통적인 VCL 뿐만 아니라 파이어몽키 등 현대식 프레임워크를 모두 지원한다. 

이 댓글 링크
다른 사이트에 공유하기

  • Kori changed the title to [튜토리얼 번역] 딜리커 (Deleaker)를 사용하여 "C++빌더" 에서 메모리 누수를 찾는 방법

이 토의에 참여하세요

지금 바로 의견을 남길 수 있습니다. 그리고 나서 가입해도 됩니다. 이미 회원이라면, 지금 로그인하고 본인 계정으로 의견을 남기세요.

Guest
이 토픽(기고/질문)에 답하기

×   서식있는 텍스트로 붙여넣기.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   이전에 작성한 콘텐츠가 복원되었습니다..   편집창 비우기

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

중요한 정보

이용약관 개인정보보호정책 이용규칙 이 사이트가 더 잘 작동하기 위해 방문자의 컴퓨터에 쿠키가 배치됩니다. 쿠키 설정 변경에서 원하는 설정을 할 수 있습니다. 변경하지 않으면 쿠키를 허용하는 것으로 이해합니다.