Я пытаюсь определить местоположение проблемы утечки памяти.
Мой проект является основанным на ATL диалоговым проектом, который использует DirectShow и стандартную библиотеку.
Я получаю в общей сложности 45 утечек памяти в своей программе, все 24 байта каждый.
У меня есть #define'd _CRTDBG_MAP_ALLOC и т.д. в моем stdafx.h, наряду с DEBUG_NEW для получения номеров документов и номеров строки для каждой из утечек памяти.
Однако никакие номера строки файла не печатаются. Блоки памяти являются всеми "нормальными" блоками и похожи на это:
{180} normal block at 0x003E6008, 24 bytes long. Data: < > _> > W > A0 AE 3E 00 B0 5F 3E 00 A0 AE 3E 00 57 00 00 00
Я попытался добавить следующую строку к запуску _tWinMain ()
_CrtSetBreakAlloc(180);
для повреждения на выделении, но отладчик не повреждается вообще.
Кто-либо может дать мне понимание, как я мог бы разыскать неуловимые утечки памяти?
Наконец, вот мой _tWinMain () - я называю _CrtDumpMemoryLeaks () непосредственно перед выходом.
int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow){
_CrtSetBreakAlloc(180);
HRESULT hRes = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
ATLASSERT(SUCCEEDED(hRes));
::DefWindowProc(NULL, 0, 0, 0L);
AtlInitCommonControls(ICC_BAR_CLASSES);
//HINSTANCE hInstRich = ::LoadLibrary(CRichEditCtrl::GetLibraryName());
hRes = _Module.Init(NULL, hInstance);
ATLASSERT(SUCCEEDED(hRes));
int nRet = Run(lpstrCmdLine, nCmdShow);
_Module.Term();
::CoUninitialize();
_CrtDumpMemoryLeaks();
return nRet;
}
Два предложения.
Во-первых, то, что создается до начала main
(или эквивалентного), уничтожается после завершения main
. Вызов _CrtDumpMemoryLeaks
в конце main
может дать ложные срабатывания. (Или это ложные отрицательные результаты?) Деструкторы глобальных объектов еще не запущены, а обратные вызовы atexit
еще не запущены, поэтому вывод утечки будет включать выделения, которые просто еще не были правильно освобождены.
(Я подозреваю, что именно поэтому ваши глобальные объекты оказываются в утечке. Вполне возможно, что в коде все в порядке, и он вполне может очищаться должным образом - просто код очистки еще не запущен, когда вызывается _CrtDumpMemoryLeaks
. )
Что вам нужно сделать вместо этого, так это направить библиотеку времени выполнения на вызов _CrtDumpMemoryLeaks
для вас, в самом конце, после завершения всех atexit
обратных вызовов и деструкторов глобальных объектов. Тогда вы увидите только настоящие утечки. Этот фрагмент поможет. Вставьте его в начало main
:
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)|_CRTDBG_LEAK_CHECK_DF);
Во-вторых, если вышеописанное обнаружит настоящие утечки из того, что выполняется до main
, вы можете применить небольшую хитрость, чтобы ваш собственный код выполнялся намного раньше, чем все остальное. Затем вы можете установить _crtBreakAlloc
до того, как произойдет выделение. Просто поместите следующий код в собственный файл .cpp:
#include <crtdbg.h>
#ifdef _DEBUG
#pragma warning(disable:4074)//initializers put in compiler reserved initialization area
#pragma init_seg(compiler)//global objects in this file get constructed very early on
struct CrtBreakAllocSetter {
CrtBreakAllocSetter() {
_crtBreakAlloc=<allocation number of interest>;
}
};
CrtBreakAllocSetter g_crtBreakAllocSetter;
#endif//_DEBUG
(Я подозреваю, что код в сегменте init компилятора может выполняться до инициализации stdin
и stdout
и т.п., и уж точно до создания любых глобальных объектов, поэтому у вас могут возникнуть трудности с выполнением чего-либо более сложного, чем описанное выше! )
(Если уж на то пошло, я придерживаюсь мнения, и уже некоторое время придерживаюсь, что распределение до начала main
- это почти всегда плохо. Трудно убирать за собой, и трудно следить за тем, что происходит. Это, конечно, удобно, но в итоге вы всегда расплачиваетесь за это позже. Впрочем, это совет, который гораздо легче раздать, чем воплотить в жизнь, особенно в составе большой команды.)
У меня была пара глобальных переменных (таблицы поиска, которые возвращали ссылки CString на сообщения об ошибках), которые я удалил из программы. Как только я это сделал - никаких утечек памяти.
Спасибо за ваши комментарии, друзья.
Интересно - мне придется исследовать другой способ реализации поиска ошибок.
Я делал что-то вроде:
CString sError = "error at line x: " + g_map.lookup(hrError);
Карта ошибок реализована как объект, который оборачивает доступ к std::map, и его деструктор работает нормально. Когда я выделяю этот объект карты на куче и удаляю его, он не сообщает об утечках памяти. Возможно, дело в том, как я конкатенирую CString....