본문 바로가기
Program/C++ MFC

[MFC / C++] 메모리 누수 확인하고 조치하기 / CPtrList 메모리 leak 현상 해제하는 방법 / normal block at error

by 냠만 2024. 5. 31.

[Memory leak현상]


Memory leak이란 프로그램에서 나 여기 쓸 거라고 공간을 확보해 놓고 사실상 명의만 바꿔놓고 사용하지 않는 공간이 되어버리는 걸 의미합니다. 운영체제 입장에서는 안 쓰는 걸 알고 있지만 언제 사용될지 모르는 공간이기 때문에 명의를 임의로 변경할 수 없는 상태로 계속 놀고 있는 공간이 되어버리는 것이지요.

지난번 포스팅한 배열 선언에서도 leak이 발생할 수 있지만 앞으로 사용하는 수많은 포인터 세계에서도 leak이 발생할 수 있음을 고려해야 합니다.
(지난 포스팅 : Delete 함수와 Delete[] 함수의 차이점을 알고 Memory leak현상 조치하기)

C++은 C#과 다르게 컴포넌트의 선언이 대화상자를 통해 구성되기 때문에 자동으로 바인딩이 되는 구조인 것 같습니다. 자동으로 묶어주니 편한 것도 있지만 해당 데이터를 다른 곳에서도 사용해야 될 경우 별도 함수를 선언해서 가져와야 하기 때문에 자칫 잘못하면 코드가 꼬여버려 분석이 어려워지는 케이스가 될 수 있을 것 같습니다.

저는 CListBox를 사용해서 값을 입력받는 간단한 예제로 테스트를 진행했습니다.

리스트박스를 준비하고 데이터를 두개정도 집어넣었습니다.


[List에서 Memory leak 발생]


List를 만들어서 특정 다이얼로그에서 값을 입력받아 리스트에 값을 추가하게 되면 리스트 구조에 따라 데이터 주소가 할당되고 메모리가 사용됩니다.
여기서 리스트의 값을 지우지 않고 프로그램을 종료하게 되면 메모리 데이터가 해제되지 않아 Memory leak현상이 발생됩니다. 이렇게 되면 별도로 해제할 수 있는 수단이 없기 때문에 재부팅을 해야 합니다.
프로그램이 종료되었으니 어디에 얼마큼의 값이 할당되어 있는지 알 수 있는 방법이 없기 때문입니다.

소스를 확인해 보면 CObject로 할당된 항목에서 메모리 사용 중이 발견되었음을 알려줍니다.
알려주기만 할 뿐 해제는 안 해주기 때문에 어디서 발생한 메모리에러인지 찾기가 어렵습니다.

줄줄이 있는 명칭 중 normal block at 메모리주소, 메모리크기 에러메시지가 메모리 누수가 발생된 지점을 의미합니다.

따라서 프로그램을 종료하거나 리스트를 초기화하는 경우 반드시 해당 메모리값을 초기화해야 합니다.
초기화하는 방법에는 크게 두 가지가 있습니다.


[RemoveAt]


첫 번째 방법인 RemoveAt을 활용해서 리스트 내부의 데이터를 삭제하는 방법입니다.

이론은 간단합니다. 리스트를 참조해서 index를 가져오고 해당 index의 포지션 정보를 가져온 다음 삭제해 주면 됩니다.


POSITION 타입은 typedef __POSITION* POSITION으로 포인터 타입입니다.
한번 설정되면 리스트에 값이 추가되거나 삭제되어 요소의 절대위치가 변경하더라도 같은 요소 추적이 가능하게 됩니다. POSITION 포인터는 List를 사용할 때 필수적으로 공부해야 하는 요소이니 숙지하시길 바랍니다.

RomoveAt 함수는 해당 인덱스의 값을 제거해 주는 역할을 합니다. 인자로 값을 받기도 하고 주소값을 받기도 합니다.


[delete]


delete는 지난번 포스팅에 이어 List의 값을 메모리에서 해제하는 역할을 합니다.


GetHeadPosition은 목록의 헤드 요소 위치를 반환합니다.
해당 포지션 데이터를 가져와 참조되는 데이터 주소를 찾아내고 삭제할 수 있습니다.
별도로 GetCount로 리스트의 개수를 참조하여 Head를 Count만큼 지우는 방법도 있습니다.


해당 리스트의 사용이 일회성이라면 완전히 해제해 버리는 방법도 있습니다. (RemoveAll)
CList에 관련된 함수는 여기를 참고하시면 됩니다.