메모리 누수의 이해
메모리 누수는 프로그램의 성능을 저하시키고 잠재적으로 시스템 불안정을 야기할 수 있는 심각한 문제입니다.
이 장에서는 C++ 프로그램에서 메모리 누수를 탐지하고 방지하는 다양한 기법과 도구를 심도 있게 살펴보겠습니다.
메모리 누수는 프로그램이 더 이상 사용하지 않는 메모리를 해제하지 않을 때 발생합니다.
이로 인해 프로그램이 점점 더 많은 메모리를 소비하게 되어 결국 시스템 자원을 고갈시킬 수 있습니다.
메모리 누수의 유형
- 단순 누수 : 할당된 메모리를 해제하지 않는 경우
- 순환 참조 누수 : 객체들이 서로를 참조하여 해제되지 않는 경우
- 캐시 누수 : 캐시된 객체를 적절히 관리하지 않는 경우
- 콜백 누수 : 등록된 콜백을 제거하지 않는 경우
메모리 누수 예제
메모리 누수 탐지 도구
Valgrind
Valgrind는 강력한 메모리 디버깅 도구입니다.
사용 방법
Valgrind 출력 분석
AddressSanitizer
AddressSanitizer는 컴파일 시간과 런타임에 메모리 오류를 탐지합니다.
사용 방법
AddressSanitizer 출력 분석
스마트 포인터를 활용한 메모리 누수 방지
스마트 포인터는 자동으로 메모리를 관리하여 많은 종류의 메모리 누수를 방지할 수 있습니다.
std::unique_ptr
std::unique_ptr
는 단일 소유권 모델을 제공합니다.
std::shared_ptr
std::shared_ptr
는 여러 포인터가 하나의 객체를 공유할 때 유용합니다.
메모리 누수 디버깅 실습
다음 코드에 있는 메모리 누수를 찾아 수정해보세요.
이 코드를 컴파일하고 Valgrind나 AddressSanitizer를 사용하여 메모리 누수를 탐지해보세요. 그리고 스마트 포인터와 RAII 원칙을 사용하여 코드를 개선해보세요.
고급 메모리 프로파일링 기법
Massif (Valgrind의 힙 프로파일러)
Massif를 사용하여 프로그램의 메모리 사용량을 시각화할 수 있습니다.
heaptrack
heaptrack은 동적 메모리 할당을 추적하고 분석하는 도구입니다.
실습 : 메모리 누수 탐지기 구현
여러분만의 간단한 메모리 누수 탐지기를 구현해보세요.
이 프로젝트를 통해 메모리 관리의 내부 동작을 깊이 이해할 수 있습니다.
- 사용자 정의 new와 delete 연산자 오버로딩
- 할당된 메모리 블록 추적
- 프로그램 종료 시 해제되지 않은 메모리 블록 보고
- 메모리 할당 위치(파일 이름, 라인 번호) 기록
- 스레드 안전성 고려
연습 문제
- 순환 참조로 인한 메모리 누수를 발생시키는 코드를 작성하고,
std::weak_ptr
를 사용하여 이를 해결해보세요.
- 메모리 풀(memory pool)을 구현하여 작은 객체들의 빈번한 할당과 해제로 인한 성능 저하를 방지해보세요.
참고자료
- "Effective Modern C++" by Scott Meyers - 특히 스마트 포인터와 메모리 관리에 관한 항목들
- "C++ Concurrency in Action" by Anthony Williams - 멀티스레드 환경에서의 메모리 관리에 대해 다룸
- Valgrind 공식 문서
- AddressSanitizer 사용 가이드
- "The C++ Programming Language" by Bjarne Stroustrup - C++의 메모리 모델과 관리에 대한 깊이 있는 설명
- CppCon 발표 영상들 - YouTube에서 "CppCon memory management"로 검색하면 다양한 고급 주제의 발표를 찾을 수 있습니다.