Студия web-дизайна Хостмэйк
Наши работыКонтактыО компанииОтзывыГлоссарийСтатьи

С++, как обнаружить утечку памяти

Статьи Программирование на С/С++

Как обнаружить утечку памяти


Введение 

При разработке больших приложений, оперирующих большими объемами информации на первое место при отладке встает проблема обнаружения неправильного распределения памяти. Суть проблемы состоит в том, что если мы выделили участок памяти, а затем освободили не весь выделенный объем, то образуются блоки памяти, которые помечены как занятые, но на самом деле они не используются. При длительной работе программы такие блоки могут накапливаться, приводя к значительному расходу памяти.


Для обнаружения подобных ошибок создано специализированное программное обеспечение (типа BoundsChecker от Numega), однако чаще бывает удобнее встроить механизм обнаружения утечки в свои проекты. Поэтому метод должен быть простым, и в то же время как можно более универсальным. Кроме того, не хотелось бы переписывать годами накопленные мегабайты кода, написанного и отлаженного задолго до того, как вам пришло в голову оградить себя от ошибок. Так что к списку требований добавляется стандартизация, т.е. нужно каким-то образом встроить защиту от ошибок в стандартный код.


Предлагаемое решение основывается на перегрузке стандартных операторов распределения памяти new и delete. Причем перегружать мы будем глобальные операторы new|delete, т.к. переписать эти операторы для каждого разработанного ранее класса было бы очень трудоемким процессом. Т.о. после перегрузки нам нужно будет только отследить распределение памяти и, соответственно, освобождение ее в момент завершения программы. Все несоответствия - ошибка.


Реализация 

Проект написан на Visual C++, но переписать его на любой другой диалект С++ не будет слишком сложной задачей. Во-первых, нужно переопределить стандартные операторы new и delete так, чтобы это работало во всех проектах. Поэтому в stdafx.h добавляем следующий фрагмент:


#ifdef _DEBUG

inline void * __cdecl operator new(unsigned int size, 

const char *file, int line)

{

};


inline void __cdecl operator delete(void *p)

{

};

#endif


Как видите, переопределение операторов происходит в блоке #ifdef/#endif. Это ограждает наш код от влияния на релиз компилируемой программы. Вы, наверное, заметили, что теперь оператор new имеет три параметра вместо одного. Два дополнительных параметра содержат имя файла и номер строки, в которой выделяется память. Это удобно для обнаружения конкретного места, где происходит ошибка. Однако код наших проектов по-прежнему ссылается на оператор new, принимающий один параметр. Для исправления этого несоответствия нужно добавиить следующий фрагмент


#ifdef _DEBUG

#define DEBUG_NEW new(__FILE__, __LINE__)

#else

#define DEBUG_NEW new

#endif

#define new DEBUG_NEW


Теперь все наши операторы new будут вызываться с тремя параметрами, причем недостающие параметры подставит препроцессор. Конечно, пустые переопределенные функции ни в чем нам не помогут, так что давайте добавим в них какой-нибудь код:


#ifdef _DEBUG

inline void * __cdecl operator new(unsigned int size,

const char *file, int line)

{

void *ptr = (void *)malloc(size);

AddTrack((DWORD)ptr, size, file, line);

return(ptr);

};

inline void __cdecl operator delete(void *p)

{

RemoveTrack((DWORD)p);

free(p);

};

#endif


Для полноты картины нужно переопределить операторы new[] и delete[], однако никаких существенных отличий здесь нет - творите!


Последний штрих - пишем функции AddTrack() и RemoveTrack(). Для создания списка используемых блоков памяти будем использовать стандартные средства
STL:


typedef struct {

DWORD address;

DWORD size;

char file[64];

DWORD line;

} ALLOC_INFO;


typedef list<ALLOC_INFO*> AllocList;


AllocList *allocList;


void AddTrack(DWORD addr, DWORD asize, const char *fname, DWORD lnum)

{

ALLOC_INFO *info;


if(!allocList) {

allocList = new(AllocList);

}


info = new(ALLOC_INFO);

info->address = addr;

strncpy(info->file, fname, 63);

info->line = lnum;

info->size = asize;

allocList->insert(allocList->begin(), info);

};


void RemoveTrack(DWORD addr)

{

AllocList::iterator i;


if(!allocList)

return;

for(i = allocList->begin(); i != allocList->end(); i++)

{

if((*i)->address == addr)

{

allocList->remove((*i));

break;

}

}

};


Перед самым завершением программы наш список allocList содержит ссылки на блоки памяти, котороые не были освобождены. Все, что нужно сделать - вывести эту информацию куда-нибудь. В нашем проекте мы выведем список неосвобожденных участков памяти в окно вывода отладочных сообщений Visual C++: 


void DumpUnfreed()

{

AllocList::iterator i;

DWORD totalSize = 0;

char buf[1024];


if(!allocList)

return;


for(i = allocList->begin(); i != allocList->end(); i++) {

sprintf(buf, "%-50s:\t\tLINE %d,\t\tADDRESS %d\t%d unfreed\n",

(*i)->file, (*i)->line, (*i)->address, (*i)->size);

OutputDebugString(buf);

totalSize += (*i)->size;

}

sprintf(buf, "--------------------------------------------------\n");

OutputDebugString(buf);

sprintf(buf, "Total Unfreed: %d bytes\n", totalSize);

OutputDebugString(buf);

};


Надеюсь, этот проект сделает ваши баг-листы короче, а программы устойчивее. Удачи!


Источник: http://realcoding.net

16.02.2006

Телефон

+7 8636 237-836

Поиск

VSESMI.ru — новости в СМИ.
Один из больших по объему информации проектов, работающих под управлением HostCMS.

Tur-Hotel.ru — отзывы об отелях
На сайте представлено описание отелей, рейтинг отелей с отзывами туристов.