Как я могу отслеживать выделение памяти в C++, особенно те, которые выполняются new / delete. Для объекта я могу легко переопределить operator new, но я не уверен, как глобально переопределить все выделения, чтобы они проходили через мои собственные new / delete. Это не должно быть большой проблемой, но я не уверен, как это должно быть сделано (#define new MY_NEW?).
Как только это сработает, я бы предположил, что достаточно иметь карту где-нибудь с указателем / местоположением выделения, чтобы я мог отслеживать все выделения, которые в настоящее время являются `` активными '', и - в конце приложения - проверять выделения которые не были освобождены.
Что ж, похоже, что это снова похоже на то, что наверняка было сделано по крайней мере несколько раз, так что какая-нибудь хорошая библиотека (желательно портативная)?
Мне понадобится решение, которое работает как минимум как в Linux, так и в Windows, а также, желательно, в Mac OS.





Не отвечая напрямую на ваш вопрос, но если вы действительно хотите получить список утекших объектов кучи в конце программы, вы можете просто запустить программу с помощью Valgrind.
Для MS VS вы можете играть с куча отладки CRT. Не так просто, как valgrind, здесь слишком много, чтобы объяснять, но может делать то, что вы хотите.
Да, я использую их в данный момент, но я хотел бы переключить распределитель памяти (особенно для отслеживания памяти в различных категориях), поэтому мне нужно здесь специальное решение.
Что ж, вы можете повторно реализовать глобальные операторы new и delete, чтобы предоставить вам нужную функциональность, но я бы не советовал этого делать, если только это не единственный способ отслеживать выделение памяти, например, из-за ограничений вашей платформы.
Отладчики памяти доступны для большинства распространенных платформ разработки. Взгляните на PurifyPlus для коммерческого решения, которое работает в Windows и различных Unix, или Valgrind для решения с открытым исходным кодом, которое работает в Linux (и, возможно, в других операционных системах, но я когда-либо использовал его только в Linux).
Если вы намереваетесь заменить глобальные операторы, взгляните на эта статья.
Вы можете использовать код http://www.flipcode.com/archives/How_To_Find_Memory_Leaks.shtml со следующими модификациями: приведенный код работает только в том случае, если у вас есть один большой исходный файл honkin. Я разобрался с этим для другого вопроса по 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 (который также # включает mymemory.h) поместите фактическое определение allocList вместе со всеми реальными функциями (а не только с прототипами) и добавьте этот файл в свой проект.
Затем #include "mymemory.h" в каждом исходном файле, в котором вам нужно отслеживать память (возможно, во всех). Поскольку в файле заголовка нет определений, вы не получите дубликатов во время ссылки, а поскольку объявления есть, вы также не получите неопределенные ссылки.
Имейте в виду, что это не будет отслеживать утечки памяти в коде, который вы не компилируете (например, сторонние библиотеки), но должно сообщить вам о ваших собственных проблемах.
Для наших проектов C++ на платформе Windows я использую VLD, Visual Leak Detector, который почти слишком легко реализовать, он отслеживает и сообщает об утечках памяти при выходе из вашего приложения - лучше всего его бесплатно и исходный код доступен. Систему можно настроить для создания отчетов несколькими способами (регистратор дисков, IDE, XML и т. д.), И она оказалась бесценной для обнаружения утечек в службах Windows, которые всегда сложно отладить. Итак, пока вы ищете портативное решение, если вы хотите использовать свое собственное, вы, конечно, можете просмотреть исходный код в качестве руководства. Надеюсь, поможет.
Чтобы процитировать сайт:
It's a very effective way to quickly diagnose, and fix, memory leaks in C/C++ applications.
Если вы собираетесь сделать это как упражнение по программированию, это может дать вам гораздо больше понимания, если вместо этого вы напишете свой собственный класс (-ы) интеллектуального указателя и последовательно будете использовать их в этом одном проекте (или модуле проекта).
В Linux есть как минимум два традиционных метода:
Таким образом, вы не только перехватываете новые и удаляете, но также и функции выделения памяти в стиле C. Я еще не делал этого в Windows, но я видел методы, позволяющие переписать то, как библиотеки DLL связаны и там (хотя я помню, что они были довольно неуклюжими).
Однако обратите внимание, что помимо того факта, что это интересные методы, я бы рекомендовал использовать valgrind, чтобы делать то, что вы хотите, прежде всего.
Если вы разрабатываете под Linux, один из лучших инструментов для этого (например, обнаружение утечек памяти, отслеживание выделений, выполненных в определенных местах кода) - это valgrind, особенно его инструмент массивов. Единственным недостатком является то, что программа работает медленнее (или намного медленнее), поэтому она полезна только для отладки.
Я заметил, что многие другие ответы сосредоточены на том, какие инструменты вы можете использовать. Я использовал некоторые из них, и они очень помогают.
Но в качестве упражнения по программированию и, увидев, что вы работаете с C++, вам нужно будет переопределить глобальные команды new и delete, а также malloc, free и realloc. Вы могли подумать, что переопределения только new и delete будет достаточно, но std :: string и другие классы, скорее всего, будут использовать malloc и особенно realloc.
Затем, когда у вас есть это на месте, вы можете начать добавлять заголовки для проверки перезаписи памяти, трассировки стека для каждого выделения и так далее.
В общем, я бы порекомендовал вам использовать один из упомянутых здесь инструментов, но было бы интересно написать свою собственную систему.
Я серьезно сомневаюсь, что строка std:; будет использовать realloc, поскольку она должна использовать предоставленный распределитель, который не поддерживает realloc.
Я бы порекомендовал вам использовать valgrind для Linux. Помимо других ошибок, таких как запись в нераспределенную память, он будет обнаруживать неосвобожденную память. Другой вариант - брызговик, который тоже сообщает о неосвободившейся памяти. Используйте опции -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, также будет использовать этот оператор удаления, поэтому это может стать сложным, если какой-то код, о котором вы не знаете, имеет перегруженный оператор new, не использующий вашу карту, потому что оператор delete сообщит вам, что он не был выделен и используйте std::free для освобождения памяти.
Также обратите внимание, как Pax также указал для своего решения, это будет показывать только утечки, вызванные кодом, использующим наш собственный определенный оператор new / delete. Поэтому, если вы хотите их использовать, поместите их объявление в заголовок и включите его во все файлы, за которыми следует следить.
Отличный пост. Мне очень помог ваш пример отследить и исправить утечку памяти во встраиваемом устройстве :)
Хороший пример! Следует отметить, что этот код не является потокобезопасным, поэтому в многопоточной среде (где new и delete будут вызываться из нескольких потоков) вам придется защитить доступ к карте track с помощью std::mutex.
Если вы разрабатываете под Windows, бесплатный инструмент DebugDiag поможет найти память и справиться с утечками.
Вам не нужно расширять свою программу, чтобы DebugDiag работал.
Хотя это не самая простая и интуитивно понятная программа! Обязательно найдите в Google учебные пособия и инструкции по его использованию.
Чтобы быть конкретным, используйте инструмент массивов 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.
Удачи!
Отличный, еще один инструмент valgrind, о котором я не знал!
Это недешево, но в дни, когда я работал на C++, я обнаруживал, что очищать - лучший инструмент для отладки утечек и других проблем с памятью (то же самое, что сейчас принадлежит IBM, так что Surport пошел вниз). Проверка границ нравился некоторым людям, но не подходил для разрабатываемого мной программного обеспечения.
Вы можете использовать файл заголовка (MemTracker.h), указанный в этом ссылка на сайт, в свое решение для отслеживания выделения / освобождения памяти в C и C++. Он показывает, есть ли у вас утечка памяти и какая строка кода отвечает за нее.
Если мне нужен инструмент, я обычно начинаю с того, что предоставляет мой компилятор / стандартная библиотека.
Проверьте этот крошечный удобный код, теперь вместо new используйте NEW и отслеживайте все выделения в конструкторе NewHelper:
#include <iostream>
class NewHelper
{
private :
void* addr = nullptr;
public :
NewHelper(void * addr_)
{
addr = addr_;
std::cout<<addr<<std::endl;
}
template <class T>
operator T ()
{
return (T)addr;
}
};
#define NEW (NewHelper)(void*)new
int main()
{
int * i = NEW int(0);
return 0;
}
Это не будет отслеживать выделения из какого-либо библиотечного кода. Более того, ваш (void*) жертвует типовой безопасностью, которую мы получаем с new.
компилятор будет преобразовывать тип с помощью NewHelper :: operator T, в любом случае я закодировал трассировщик памяти fullc / C++, который отслеживает каждое выделение в ваших файлах и файлах stdlib ...., я могу продать его, если кому интересно, функции: - Журнал трассировки стека для всех распределений, которые никогда не освобождаются: - Журнал трассировки стека для всех выделений, свободных более одного раза, -Stacktrace для распределений, недействительных free () ... - отображение трассировки стека для всех распределений, произошедших в конструкторе атрибутов, когда родительский объект выделен, но никогда удалено (конструктор не вызывается)
Нет универсального готового ответа. Пожалуйста, предоставьте дополнительную информацию об используемой ОС и платформе.