Борьба с утечками памяти - PullRequest
2 голосов
/ 27 января 2011

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

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

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

Ответы [ 8 ]

4 голосов
/ 27 января 2011

Я использовал Valgrind для Linux, но вы можете найти несколько полезных ссылок здесь Есть ли хорошая замена Valgrind для Windows?

4 голосов
/ 27 января 2011

Умные указатели - это, вероятно, то, что вы ищете.По крайней мере, при выделении объектов.

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

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

2 голосов
/ 27 января 2011

Прежде чем идти по пути отладки, давайте рассмотрим проект.

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

  1. не использовать new
  2. не использовать delete

Поначалу это может показаться удивительным:)

1. Не используйте new

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

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

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

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

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

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

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

3. Код отзыва

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

Примечание: единственное место для delete - это когда ВЫ пишете умный менеджер. Обычно нет особого смысла, учитывая широкий выбор. Однако, если вы это сделаете, помните, что это область гуру (исключение безопасности, ...); Единственный совет, который я могу дать, это то, что умный менеджер делает только одно: он умело управляет ЕДИНСТВЕННЫМ ресурсом. Попробуйте объединить это с чем-то другим, и у вас не получится, и вы даже не поймете это

2 голосов
/ 27 января 2011

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

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

    #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);
    

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

test:

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

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

больше информации в MSDN

2 голосов
/ 27 января 2011

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

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

замените boost на std::tr1, если выневозможно использовать повышение.Дополнительная документация доступна на веб-сайте Boost.

1 голос
/ 27 января 2011

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

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

1 голос
/ 27 января 2011

Я прохожу свою программу и сокращаю использование умных указателей, а также синглетонов.

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

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

0 голосов
/ 27 января 2011

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

Вместо:

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 нуждается в куче.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...