Ошибка: Failed to parse the Currency Converter XML document.
$58 854.54


Ошибка: Failed to parse the Currency Converter XML document.
$863.73


Ошибка: Failed to parse the Currency Converter XML document.
$587.16


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

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

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

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


Для обнаружения подобных ошибок создано специализированное программное обеспечение (типа 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;
    DWORDsize;
    charfile[64];
    DWORDline;
} ALLOC_INFO;

typedef list 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:\tLINE %d,\tADDRESS %d %d unfreed»,
      (*i)->file, (*i)->line, (*i)->address, (*i)->size);
OutputDebugString(buf);
totalSize += (*i)->size;
}
sprintf(buf, «--------------------------------------------------»);
OutputDebugString(buf);
sprintf(buf, «Total Unfreed: %d bytes», totalSize);
OutputDebugString(buf);
};

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

 

Интересное

Три прикола в Дельфи!
Сейчас я покажу, как своими руками написать три прикола в DELPHI. Итак, приступим сразу к делу.Прикол первый:Давайте напишем программу, которая будет выводить в какую-то часть экрана кнопку с...
Подробнее...
Управление сетями. Правила...
Идея создания сетей для передачи данных на большие и не очень большие расcтояния витала в воздухе с той самой поры, как человек впервые задумался над созданием телекоммуникационных устройств. В...
Подробнее...
Определение наличия...
По статистике, около 90% пользователей в наши дни пользуются Internet Explorer (5-й или 6-й версией). В этих браузерах Flash, как правило, уже установлен (если же нет, то, при наличии...
Подробнее...
Безопасность Windows XP SP3
Реальность такова, что не все организации перешли на использование Windows Vista. С выпуском Windows XP (начатым в 2001, и данная ОС все еще продается) компания Microsoft выпустила третий пакет...
Подробнее...
Эксплойты
Эксплойт (англ. exploit — использовать) — это общий термин в сообществе компьютерной безопасности для обозначения фрагмента программного кода который, используя возможности предоставляемые...
Подробнее...
Наиболее распространенне...
Количество случаев похищения идентификационных данных все растет. Нашла ли ваша организация решение этой проблемы или она остается для вас по-прежнему актуальной? Личные идентификационные данные...
Подробнее...
Как за нами шпионят
Spyware – это термин, определяющий приложения, которые записывают информацию о поведении пользователя в сети Интернет и сообщают об этом своим создателям. Результатом их действия может стать как...
Подробнее...
Шесть правил ухода за...
Мало кому приходит в голову протирать экран телевизора спиртом — так почему же у многих чешутся руки произвести подобное издевательство над монитором?!Причем можно было бы понять, если бы это...
Подробнее...
Список служб, которые, в...
automatic updates (Автоматичесткое обновление). Учитывая, что обновлять систему можно и вручную,имеет смысл отключить эту службу. Особенно в том случае, если нет постоянного соединения с...
Подробнее...
Изменение регистра
В Delphi есть три функции для изменения регистра: upcase, lowercase, uppercase. Но они работают только для латинского алфавита. Чтобы сделать аналогичные функции для русского алфавита я...
Подробнее...