2011-01-27 2 views
2

Утечки памяти - это кошмар. Я знаю: у меня есть.Борьба с утечками памяти

Что является наиболее эффективным (наименее болезненным и трудоемким) способом их нахождения?

Я использую Visual Studio 2010 и разрабатываю в C++ для Windows.

+0

В зависимости от того, что вы программируете, более разумная реализация algotihms и т. Д. Может предотвратить множество проблем. На мой взгляд, лучше предупредить заранее, чем распределить их после этого. – pimvdb

+4

Я думаю, что он имел в виду 'locate', а не' allocate': D – Elalfer

+0

@ Elalfer, мои извинения за «выделение».Я имел в виду «найти» :-) @ Майкл Петротта, спасибо за изменения в моем вопросе. –

ответ

4

Smart Pointers, вероятно, то, что вы ищете. По крайней мере, при распределении объектов.

Для контейнеров используйте STL, где это возможно.

Если вы ищете существующие утечки памяти, вы можете использовать DEBUG_NEW, если находитесь в VS2010.

+0

+1 Привет, Джеймс, спасибо за ваш ответ. Особенно, DEBUG_NEW ценен для меня. Я проверю. –

+0

Я отмечаю этот ответ как * Принятый ответ * для этого вопроса. Это правда, что этот ответ является самым коротким, однако он содержит большинство советов, упомянутых ниже. –

+0

@ Bunkai.Satori: Надеюсь, это сработает хорошо для вас. – James

2

если в данный момент ваш код имеет много звонков на new и delete, и вы можете использовать импульс (или TR1), следующие типы будут очень полезны в протечках боевых

  • boost::shared_ptr
  • boost::scoped_ptr
  • boost::shared_array
  • boost::scoped_array

замените boost на std::tr1, если вы не можете использовать boost. Более documentation доступен на сайте повышения.

+1

VS2010 имеет классы TR1, не нужно использовать boost – ThomasMcLeod

+0

+1 - Привет, Сэм, спасибо за ваш ответ. Да, у моего кода действительно много новых и удаленных. Я проверю библиотеку boost. –

+0

@ThomasMcLeod, не могли бы вы поделить немного, что такое классы TR1, пожалуйста? –

2

для MSVC Я использую следующую технику:

  1. использовать прекомпилирована заголовки (например stdafx.h) в в самом начале stdafx.h поставил:

    #if defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER) 
        #define _CRTDBG_MAP_ALLOC 
        #include <crtdbg.h> 
        #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) 
    #endif 
    
  2. затем включить все заголовки вы не собираетесь изменять (или очень редко). точка здесь, чтобы включить заголовки с перегруженной operator new

  3. в конце stdafx.h положить:

    #if defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER) 
        #if defined(new) 
         #undef new 
        #endif 
        #define new DEBUG_NEW 
    #endif 
    
  4. в вашем main() поставил

    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); 
    

    в качестве первой линии.

тест:

int main() 
{ 
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF/* | _CRTDBG_CHECK_CRT_DF*/); 
    int* i = new int(); 
    std::vector<int>* v = new std::vector<int>(1000); 
    return 0; 
} 

запустить это в пошаговом режиме.в окне вывода VC вы найдете что-то вроде:

The thread 'Win32 Thread' (0x1b1c) has exited with code 0 (0x0). 
Detected memory leaks! 
Dumping objects -> 
{97} normal block at 0x02725E38, 4000 bytes long. 
Data: <    > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
c:\main.cpp(7) : {96} normal block at 0x02725DE8, 20 bytes long. 
Data: <  8^r mr > 00 00 00 00 CD CD CD CD 38 5E 72 02 D8 6D 72 02 
c:\main.cpp(6) : {95} normal block at 0x02725DA8, 4 bytes long. 
Data: < > 00 00 00 00 
Object dump complete. 
The program '[3756] test.exe: Native' has exited with code 0 (0x0). 

, как вы можете видеть, вы 3 memleaks: два в main() и один где-то в другом месте. «где-то еще» не обнаружено, потому что оно находится в <vector>, и оно было включено до нашего #define new DEBUG_NEW. этот отчет достаточно удобен, потому что после каждого запуска в отладчике вы видите, появились ли новые memleaks, и вы можете просто дважды щелкнуть соответствующий memleak, чтобы перейти к точному месту в редакторе. {97}, {96} и т. Д. Номера в примере указывают порядковый номер выделения памяти, вы можете использовать его для запуска соответствующего распределения памяти путем помещения _CrtSetBreakAlloc(memalloc_seq_no);до это распределение фактически происходит.

Техника далеко не идеальная и универсальная, но я нашел ее очень полезной в повседневном программировании в VC. более

Информация в MSDN

+0

+1 Привет, Энди, очень приятная душа. Было бы хорошо, если бы мы могли быть проинформированы о каждой утечке памяти, в том числе в . –

+0

вам сообщают, просто без имени файла: lineno info. actall вы редко нуждаетесь в этом, потому что '' не имеет memleaks, это наш код. как я указал, ограничение состоит в том, что вы должны включать файлы с объявленным 'operator new' перед' #define new DEBUG_NEW'. в особых случаях вы можете использовать '_CrtSetBreakAlloc', чтобы найти точное место размещения памяти. –

+0

Энди, да, вы правы, что предназначен не для создания утечек памяти. –

1

Я иду через мою программу и сокращение использования смарт-указатели, а также одиночек.

Идея заключается в использовании указателей и интеллектуальных указателей только при необходимости.

Я очистил много утечек памяти, используя эти два метода. Например, если классу нужен доступ к singleton, преобразуйте singleton в обычный объект и передайте его в качестве аргумента. Это имеет побочный эффект, показывающий истинные требования методов и класса, а не скрытые требования к синглтон.

+0

Если я правильно понимаю, использование интеллектуальных указателей - не лучшее решение для утечек памяти? По-вашему, использует ли утечки информации о утечке памяти лучшую альтернативу? –

+0

@ Бункай, позвольте мне уточнить. Без каких-либо динамических объектов не может быть утечек памяти. Каждое распределение динамического объекта имеет потенциал для утечки памяти. Умные указатели уменьшают вероятность утечек. Умные указатели - лучшее решение, которое является обычным (немым) указателем. Предпочитают локальное распределение стека для динамического распределения. Предпочитают передавать по ссылке, чтобы пройти по указателю. Предпочитайте интеллектуальные указатели на обычные указатели. –

+0

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

0

Хороший план мирового господства с утечками памяти - не использовать кучу вообще.

Вместо:

class A { A() { m_a = new int; } ~A() { delete m_a; } int *m_a; }; 

всегда можно использовать:

class A { int m_a; }; 

и это значительно проще и более удобно.

Он также работает в других ситуациях, вместо:

MyObject *create_obj(int a) { 
    switch(a) { 
     case 0: return new MyDerived1; 
     case 1: return new MyDerived2; 
    }; 
} 
int main() { MyObject *o = create_obj(1); o->do_x(); o->do_y(); delete o; } 

Может всегда использовать это:

class Obj { 
    Obj(int a) : a(a) { } 
    void do_x() { switch(a) { case 0: ... break; case 1: ... break; }; } 
    void do_y() { switch(a) { case 0: ... break; case 1: ... break; }; } 
    int a; 
}; 
int main() { Obj o(1); o.do_x(); o.do_y(); } 

Копирование большую структуру данных из сферы в другую для выделения стека работает следующим образом:

void Collect(MyStorage &s) 
{ 
    MyObject o; 
    MyObject2 o2(o); 
    MyObject3 o3(o2); 
    s.Copy(o3); 
} 
void f(const MyStorage &s); 
int main() { MyStorage s; Collect(s); f(s); } 

Таким образом, создание большой структуры данных в стеке будет работать нормально. Класс Storage требует кучи.

+0

Первый, конечно, не тот, второй - неуклюжий ... –

+0

Это просто примеры того, как избежать использования новых. Это должно быть соглашение, которое будет использоваться при написании кода. Укрепление его позже значительно сложнее. – tp1

+0

Хранение структур данных лагере на стеке очень болезненно. – crazylammer

1

Если вы можете воспроизвести проблему, я рекомендую использовать umdh из пакета инструментов отладки окон. Если вы включаете трассировки стека пользовательского режима с помощью gflags или верификатора приложения для своего исполняемого файла, он может принимать до и после моментальных снимков всех ваших распределений памяти, которые затем могут использоваться для их отслеживания. Вы также можете подключить с помощью windbg и использовать кукушку -l, чтобы перечислять любые не связанные ссылки и можете печатать их с помощью! Heap -p -a.

Вы также можете использовать инструменты статического анализа, такие как Coverity (не уверены, есть ли какие-либо хорошие бесплатные альтернативы), но это может не уловить любую ситуацию.

+0

Привет, Dan. Спасибо, и +1 за новые инструменты: UMDH и Windbg. Я не знаю этих пакетов хорошо, но у меня есть интуиция, что это внешние инструменты. Поэтому они не дают вам информацию о исходном коде, где искать утечку памяти, верно ли это? Поправьте меня, если я ошибаюсь. С точки зрения экономии времени, более практично иметь информацию прямо из исходного кода с именем файла и файловой линией, которая генерирует утечки памяти. –

+0

Я думаю, что если у вас есть индивидуальные настройки символов, обе инструменты могут предоставить файл/строки, но они могут определенно получить вам функции и трассировки стека. Мне нравится использовать эти инструменты, потому что для их использования не требуется никаких модификаций кода. Средство microsoft leakdiag может делать то же самое, что и распределение из источников, а не из-за кука-менеджера по умолчанию, однако я менее знаком с его использованием. – Dan

2

Перед тем, как отправиться на отладочную дорогу, давайте рассмотрим дизайн.

Есть два правила, чтобы избежать кардинальных утечек памяти:

  1. не используют new
  2. не используют delete

Это может показаться удивительным на первый :)

1. Не использовать new

Вы не должны выделять в куче всякий раз, когда вы можете избежать его:

  • выделить на стеке
  • использовать контейнеры, которые будут обрабатывать на выделение памяти для вас

Каждый new удалить из вашего кода, обязательно утечка памяти, которой у вас не будет.

2. Не используйте delete

Проблема заключается в том, что соответствие каждого new с delete крайне сложно. Трудно забыть удалить его, трудно не удалять его дважды.

C++ предлагает удобное решение в виде SBRM (облачное управление ресурсами, также известное аббревиатурой RAII, чье имя довольно вводит в заблуждение). Это, в частности, означает умных менеджеров.

Каждый раз, когда вы на самом деле звоните по номеру new, не забудьте немедленно передать ответственность за управление вновь выделенным объектом либо умным указателем, либо каким-либо другим одинаково умным контейнером (например, boost::ptr_vector).

3. Код отзыва

  • Вопрос каждое использование new
  • Убедитесь, что каждый new результат сразу же помещают в смарт-менеджер

Примечание: единственное место для delete - это когда вы пишете умного менеджера. Обычно этого не происходит, учитывая широкий выбор. Однако, если вы это сделаете, помните, что это область гуру (безопасность исключений, ...); один совет, который я могу дать, заключается в том, что умный менеджер делает только одно: он эффективно управляет SINGLE-ресурсом. Попытайтесь объединить его с чем-то другим, и вы не справитесь, и вы даже не поймете это.

+0

+1 Привет, Matthieu, спасибо за полезные советы. Хотелось бы избежать использования * new *, * delete *, поскольку я беру данные из внешних файлов, которые определяют, сколько объектов мне нужно создать. Если до сих пор я не использовал интеллектуальные указатели в своем проекте, потребуется много рефакторинга для их реализации в середине проекта, верно ли, пожалуйста? –

+0

@Bunkai: Это зависит от того, есть ли четкое владение для каждого объекта или нет, но это может занять некоторое время. С другой стороны ... правильность бесценна, не так ли?)? –

+0

Я обязательно рассмотрю использование умных указателей .. :-) Спасибо –

Смежные вопросы