Подтвердить что ты не робот

Как отслеживать выделения памяти в С++ (особенно new/delete)

Как я могу отслеживать выделения памяти в С++, особенно те, которые выполняются с помощью new/delete. Для объекта я могу легко переопределить operator new, но я не уверен, как глобально переопределить все распределения, чтобы они прошли через мой пользовательский new/delete. Это не должно быть большой проблемой, но я не уверен, как это должно быть сделано (#define new MY_NEW?).

Как только это сработает, я бы предположил, что достаточно иметь карту где-то из указателя/местоположения выделения, поэтому я могу отслеживать все распределения, которые в настоящее время являются "активными" и - в конце приложения - проверьте выделение, которые не были освобождены.

Ну, это похоже на то, что, безусловно, было сделано несколько раз, по крайней мере, так что любая хорошая библиотека там (желательно переносная)?

4b9b3361

Ответ 1

Я бы порекомендовал вам использовать valgrind для linux. Он поймает не освобожденную память, среди других ошибок, таких как запись в нераспределенную память. Другим вариантом является mudflap, в котором также рассказывается об освобожденной памяти. Используйте опции -fmudflap -lmudflap с gcc, затем запустите свою программу с помощью MUDFLAP_OPTIONS=-print-leaks ./my_program.

Вот очень простой код. Он не подходит для сложного отслеживания, но предназначен для того, чтобы показать вам, как вы это сделаете в принципе, если бы вы сами его реализовали. Что-то вроде этого (оставленные вещи, вызывающие зарегистрированный new_handler и другие детали).

template<typename T>
struct track_alloc : std::allocator<T> {
    typedef typename std::allocator<T>::pointer pointer;
    typedef typename std::allocator<T>::size_type size_type;

    template<typename U>
    struct rebind {
        typedef track_alloc<U> other;
    };

    track_alloc() {}

    template<typename U>
    track_alloc(track_alloc<U> const& u)
        :std::allocator<T>(u) {}

    pointer allocate(size_type size, 
                     std::allocator<void>::const_pointer = 0) {
        void * p = std::malloc(size * sizeof(T));
        if(p == 0) {
            throw std::bad_alloc();
        }
        return static_cast<pointer>(p);
    }

    void deallocate(pointer p, size_type) {
        std::free(p);
    }
};

typedef std::map< void*, std::size_t, std::less<void*>, 
                  track_alloc< std::pair<void* const, std::size_t> > > track_type;

struct track_printer {
    track_type * track;
    track_printer(track_type * track):track(track) {}
    ~track_printer() {
        track_type::const_iterator it = track->begin();
        while(it != track->end()) {
            std::cerr << "TRACK: leaked at " << it->first << ", "
                      << it->second << " bytes\n";
            ++it;
        }
    }
};

track_type * get_map() {
    // don't use normal new to avoid infinite recursion.
    static track_type * track = new (std::malloc(sizeof *track)) 
        track_type;
    static track_printer printer(track);
    return track;
}

void * operator new(std::size_t size) throw(std::bad_alloc) {
    // we are required to return non-null
    void * mem = std::malloc(size == 0 ? 1 : size);
    if(mem == 0) {
        throw std::bad_alloc();
    }
    (*get_map())[mem] = size;
    return mem;
}

void operator delete(void * mem) throw() {
    if(get_map()->erase(mem) == 0) {
        // this indicates a serious bug
        std::cerr << "bug: memory at " 
                  << mem << " wasn't allocated by us\n";
    }
    std::free(mem);
}

int main() {
    std::string *s = new std::string;
        // will print something like: TRACK: leaked at 0x9564008, 4 bytes
}

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

Убедитесь, что если вы переопределили оператор new, вы используете карту для регистрации ваших распределений. Удаление памяти, выделенной формами размещения new, также будет использовать этот оператор delete, поэтому может оказаться сложным, если какой-либо код, который вы не знаете, перегрузил оператора new, не используя вашу карту, потому что удаление оператора сообщит вам, что оно не было выделено и используйте std::free, чтобы освободить память.

Также обратите внимание, что Pax также указал на его решение, это будет показывать только те утечки, которые вызваны кодом, используя наш собственный определенный оператор new/delete. Поэтому, если вы хотите их использовать, поместите свое объявление в заголовок и включите его во все файлы, которые следует просматривать.

Ответ 2

Чтобы быть конкретным, используйте инструмент valgrind массива. В отличие от memcheck, massif не занимается незаконным использованием памяти, а отслеживает выделение с течением времени. Он отлично справляется с "эффективным" измерением использования памяти кучи в программе. Лучшая часть, вам не нужно писать какой-либо код. Попробуйте:

http://valgrind.org/docs/manual/ms-manual.html

Или если вы действительно нетерпеливы:

valgrind --tool=massif <executable> <args>
ms_print massif.out.<pid> | less

Это даст вам график распределений с течением времени и обратные трассы туда, где возникли большие распределения. Этот инструмент лучше всего работает в Linux, я не знаю, есть ли переменная Windows. Он работает на OS X.

Удачи!

Ответ 3

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

Отладки памяти доступны для большинства общих платформ разработки. Посмотрите PurifyPlus для коммерческого решения, которое работает на Windows и различных Unix или valgrind для открытого исходного кода, который работает в Linux (и, возможно, в других операционных системах, но я использовал его только в Linux).

Если вы намерены заменить глобальные операторы, посмотрите эту статью.

Ответ 4

Вы можете использовать код http://www.flipcode.com/archives/How_To_Find_Memory_Leaks.shtml со следующими изменениями: приведенный ниже код работает только в том случае, если у вас есть один большой хонкин ' исходный файл. Я отсортировал это для другого вопроса о SO (здесь).

Для начала не изменяйте stdafx.h, внесите изменения в свои собственные файлы.

Создайте отдельный файл заголовка mymemory.h и, например, разместите в нем свои прототипы функций (обратите внимание, что это не имеет тела):

inline void * __cdecl operator new(unsigned int size,
    const char *file, int line);

Также в этом заголовке добавьте другие прототипы для AddTrack(), DumpUnfreed() и т.д. и #defines, typedef и оператор extern:

extern AllocList *allocList;

Затем в новом mymemory.cpp(который также #include mymemory.h) поместите фактическое определение allocList вместе со всеми реальными функциями (а не только прототипами) и добавьте этот файл в свой проект.

Затем #include "mymemory.h" в каждом исходном файле, в котором вам нужно отслеживать память (возможно, все из них). Поскольку в файле заголовка нет определений, вы не будете получать дубликаты во время ссылки и потому, что объявления там, вы также не получите ссылки undefined.

Имейте в виду, что это не будет отслеживать утечки памяти в коде, который вы не компилируете (например, сторонние библиотеки), но он должен сообщить вам о ваших собственных проблемах.

Ответ 5

Для наших проектов на платформе С++ на платформе Windows я использую VLD, Visual Leak Detector, который почти слишком прост для реализации этих треков и отчетов об утечке памяти при выходе из вашего приложения - что лучше всего доступно, и источник доступен. Система может быть настроена для отчетов несколькими способами (дисковый регистратор, IDE, XML и т.д.) И была бесценна для обнаружения утечек в службах Windows, которые всегда являются проблемой для отладки. Поэтому, пока вы ищете портативное решение, если вы хотите сворачивать самостоятельно, вы можете, конечно, просмотреть источник для руководства. Надеюсь, что это поможет.

Чтобы процитировать сайт:

Это очень эффективный способ быстро диагностировать и исправлять утечки памяти в Приложения C/С++.

http://dmoulding.googlepages.com/vld

Ответ 6

В Linux есть как минимум два традиционных метода:

  • malloc() и free() (и другие связанные с памятью функции) являются слабыми символами, а это означает, что вы можете просто переопределить их, и ваши версии будут использоваться. Пример реализации: см. Электрический забор.
  • С переменной окружения LD_PRELOAD вы можете переопределять символы (как слабые, так и сильные) в общих библиотеках с символами, найденными в библиотеках, содержащихся в переменной среды LD_PRELOAD. Если вы скомпилируете общую библиотеку с malloc(), free() и друзьями, вы все настроены. Опять же, электрический забор демонстрирует это.

Таким образом, вы не только захватываете новые и удаляете, но также и функции выделения памяти в стиле C. Я еще не делал этого на окнах, но я видел способы переписать, как DLL связаны там тоже (хотя я помню, что они были довольно неуклюжими). ​​

Обратите внимание, что кроме того, что это интересные методы, я бы рекомендовал использовать valgrind, чтобы делать то, что вы хотите выше всего.

Ответ 7

Если вы работаете под Windows, бесплатный инструмент DebugDiag поможет найти память и обработать утечки.

Вам не нужно расширять свою программу для работы DebugDiag.

http://www.microsoft.com/downloads/details.aspx?FamilyID=28BD5941-C458-46F1-B24D-F60151D875A3&displaylang=en

Хотя это не самая простая или самая интуитивная программа! Удостоверьтесь, что вы Google для учебных пособий и инструкций о том, как их использовать.

Ответ 9

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

Для MS VS вы можете играть с Отладочной CRT-кучей. Не так просто, как valgrind, слишком много для объяснения здесь, но может делать то, что вы хотите.

Ответ 10

Если вы разрабатываете Linux, один из лучших инструментов для этого (например, обнаружение утечек памяти, отслеживание распределения в определенных местах кода) - valgrind, особенно его инструмент массива. Единственным недостатком является то, что программа работает медленнее (или намного медленнее), поэтому она полезна только для отладки.

Ответ 11

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

Но как программирование, и, увидев, что вы работаете с С++, вам нужно переопределить глобальные новые и удалить, а также malloc, free и realloc. Вы могли бы подумать, что только переопределение новых и удаления будет достаточно, но std::string и другие классы, скорее всего, будут использовать malloc и особенно realloc.

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

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

Ответ 12

Это не дешево, но я использовал для своих дней С++, что purify был лучшим инструментом для отладки утечек и другой памяти (теперь он принадлежит IBM, поэтому surport спустился вниз). Bounds Checker понравился некоторыми людьми, но не работал хорошо для программного обеспечения, которое я разрабатывал.

Ответ 13

Вы можете использовать добавить заголовочный файл (MemTracker.h), указанный в этом ссылка в ваше решение для отслеживания выделения/освобождения памяти в C и С++. Он показывает, есть ли у вас утечка памяти и какая строка кода отвечает за нее.

Ответ 14

Если мне нужен инструмент, я обычно начинаю с того, что предоставляет моя компилятор/стандартная библиотека.

  • Если вы используете glibc, вы можете использовать mtrace. Он устанавливает глобальный крючок, который регистрирует каждую функцию распределения памяти glibc (malloc, realloc, memalign, free и все, что реализовано поверх них, как new/delete).
  • Если вы используете Microsoft CRT, вы можете посмотреть CRT Debug Heap Details. Существуют примеры того, как устанавливать отладочную версию функций распределения памяти, получать статистику кучи, находить утечки памяти и т.д.

Ответ 15

Если вы хотите сделать это как упражнение по программированию, это может дать вам гораздо больше возможностей для написания собственного класса интеллектуальных указателей и последовательно использовать их в этом одном проекте (или модуле проекта).