Access Violation Error in MFC
Release 모드로 프로그램을 완성하고 빌드하여 다른 폴더에 실행파일을 복사해서 실행한 결과 잠시 후 아무런 메시지도 없이 프로그램이 중단된다는 경고창이 나타나고 프로그램이 종료되었다. 현재로서는 그 이유에 대해서 아무런 단서도 없고 다만 포인터 변수 안에 저장된 주소가 어디선가 본의 아니게 변경되었거나 접근하고자 하는 인스턴스나 값에 대한 주소가 사라지고 NULL 값이 그 자리를 차지하고 있을 것으로 생각하고 있었다. 메모리 누수를 생각할 수도 있었지만 먼저 메모리 누수에 대해서는 이미 필요한 작업을 마친 상태라서 문제의 원인에서 제외하기로 하였다.
디버깅 모드에서 테스트할 필요가 있었지만 현재의 개발환경은 처음부터 Release모드였으며 사용되는 라이브러리 들이 모두 Release모드에서만 실행되는 관계로 Debug 모드가 아닌 Release 모드에서 디버깅을 해야하는 상황이 된 것이다.
프로젝트 설정을 Release 모드로 그대로 두고 F5 (Start Debugging)을 눌러 디버깅을 시작했고 잠시 후 경고창이 뜨면서 프로그램이 중단되었는데 중단된 간략한 메시지가 다음과 같이 출력되었다.
Unhandled exception at 0xFEEEFEEE in XPACSViewer.exe: 0xC0000005:
Access violation executing location 0xFEEEFEEE.
그와 동시에 에디터에는 중단된 코드상의 위치에 화살표가 표시되어 있었고 에디터 하단에 있는 Call Stack에는 현재의 오류가 발생한 함수에 이르기까지의 호출된 함수의 리스트가 순서대로 나타나 있었고 맨 위에는 현재 멈춰진 함수의 이름이 위치해 있었고 화살표로 표시되어 있었다. 그런데 Call Stack 상의 화살표가 가리키는 행 바로 위에는 다음과 같은 메시지도 있었다
feeefeee()
[Frames below may be incorrect and/or missing]
Call Stack의 왼쪽에 있는 Autos 라는 창에는 멈춰진 코드 위치에서의 변수들의 현재 상태가 출력되어 있었다
에디터 상에 표시된 화살표는 다음과 같은 라인을 가리키고 있었다
OFCondition ofCond = dcmFileFormat->getDataset()->findAndGetOFString(tag, value);
프로그램이 멈추면서 나타난 경고창과 Call Stack에는 특정 메모리의 주소(0XFEEEFEEE)를 지적하면서 그 곳에 접근하는 것은 오류라고 알려주고 있다. 그 주소가 어쩐지 익숙한 생각이 들어서 검색해보니 다음과 같이 결정적인 단서를 발견하고 말았다.
FEEEFEEE Used by Microsoft's HeapFree() to mark freed heap memory
That is, the problem comes up from the fact that the code deals with released memory as if it is still alive. To isolate the issue to specific code fragment, debug and use call stack information at the time of exception.
그런즉, 힙에서 지워진 변수의 영역은 0xFEEEFEEE 으로 채워진다는 것이니, 코드 어디선가 삭제된 힙 메로리를 참조하고 있는 부분이 있다는 의미였다. 오류가 발생했다고 Call Stack과 에디터가 화살표로 가리키는 라인을 검사하여 메모리를 해제한 사실를 기억해 낼 수 있었고 getDataset() 이란 함수가 리턴한 인스턴스를 한번 사용하고 메모리관리 차원에서 제거한 것이 그만 화가 되었던 것이다. 메모리를 해제한 곳을 찾아 주석으로 처리하니 그냥 해결되고 말았다. 누구에겐가 감사를 해야 하는데 아무래도 0xFEEEFEEE 주소가 갖는 의미에 대해서 잘 설명해 놓은 stackoverflow.com 과 글의 저자 Roman R. 씨에게 감사드립니다. Thanks Roman !
메모리 누수에 대한 검사방법을 잘 설명한 페이지
메모리 누수의 확인은 일반 함수를 사용할 경우에는 확인할 수 없고 개발자가 메모리를 동적으로 할당하는 경우에는 그 파일명과 행번호를 메모리에 기억하고 있다가 프로그램 종료시 까지도 메모리를 해제하지 않는 경우에는 경고를 출력하는 개념으로 작동한다는 것이다. 그러므로 메모리 누수를 확인하기 위해서는 동적할당을 할 당시부터 일반함수가 아닌 누수확인용 함수를 사용해야 한다는 점을 이해하여야만 아래의 작업들 또한 이해가 쉬월하게 된다.