Как найти утечку памяти в C++

Я думаю, что здесь есть два вопроса.


В чем разница между throw и throw e;?

Я не думаю, что когда-либо есть веская причина написать catch (Exception e) { throw e; }. Это теряет оригинальную трассировку стека. При использовании throw; исходная трассировка стека сохраняется. Это хорошо, потому что это означает, что причину ошибки легче найти.


В чем разница между catch и catch (Exception e)?

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

Переменная 'e' объявлена, но никогда не использовалась

Имеет смысл задать этот вопрос, если у вас есть какой-то другой код в блоке catch, который действительно делает что-то полезное. Например, вы можете захотеть записать исключение:

try
{
    int value = 1 / int.Parse("0");
}
catch (Exception e)
{
    LogException(e);
    throw;
}

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

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

7
задан 18 June 2009 в 22:38
поделиться

15 ответов

Один из способов - вставить строки имени файла и номера строки (через указатель) модуля, выделяющего память, в выделенный блок данных. Номер файла и строки обрабатывается с помощью стандартных макросов C ++ « __ FILE __ » и « __ LINE __ ». Когда память освобождается, эта информация удаляется.

Одна из наших систем имеет эту функцию, и мы называем ее «отчет о потере памяти». Таким образом, в любой момент из нашего интерфейса командной строки мы можем распечатать всю выделенную память вместе с большим списком информации о том, кто выделил память. Этот список отсортирован по тому модулю кода, которому выделено больше всего памяти. Мы часто будем отслеживать использование памяти таким образом с течением времени, и в конечном итоге утечка памяти всплывет вверх в списке.

3
ответ дан 6 December 2019 в 06:25
поделиться

Используйте умные указатели и никогда не думайте об этом снова, существует множество официальных типов, но довольно легко создать свой собственный:

class SmartCoMem
{
public:
SmartCoMem() : m_size( 0 ), m_ptr64( 0 ) {
}

~SmartCoMem() {
    if( m_size )
        CoTaskMemFree((LPVOID)m_ptr64);
}

void copyin( LPCTSTR  in, const unsigned short size )
{
    LPVOID ptr;
    ptr = CoTaskMemRealloc( (LPVOID)m_ptr64, size );
    if( ptr == NULL )
        throw std::exception( "SmartCoMem: CoTaskMemAlloc Failed" );
    else
    {
        m_size = size;
        m_ptr64 = (__int64)ptr;
        memcpy( (LPVOID)m_ptr64, in, size );
    }
}

std::string copyout( ) {
    std::string out( (LPCSTR)m_ptr64, m_size );
    return out;
}

__int64* ptr() {
    return &m_ptr64;
}

unsigned short size() {
    return m_size;
}

unsigned short* sizePtr() {
    return &m_size;
}

bool loaded() {
    return m_size > 0;
}

private:
    //don't allow copying as this is a wrapper around raw memory
    SmartCoMem (const SmartCoMem &);
    SmartCoMem & operator = (const SmartCoMem &);

__int64 m_ptr64;
unsigned short m_size;

};

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

0
ответ дан 6 December 2019 в 06:25
поделиться

Для подобного тестирования попробуйте скомпилировать встроенный код для Linux (или любой другой ОС, которую вы используете) и используйте хорошо зарекомендовавший себя инструмент, такой как Valgrind, для проверки утечек памяти. Это может быть проблемой, но вам просто нужно заменить любой код, который напрямую обращается к оборудованию, на код, имитирующий что-то подходящее для вашего тестирования.

Я обнаружил, что использование SWIG для преобразования моего встроенного кода в Linux - родная библиотека и запуск кода из скрипта Python действительно эффективен. Вы можете использовать все инструменты, доступные для невстроенных проектов, и протестировать весь свой код, кроме драйверов оборудования.

0
ответ дан 6 December 2019 в 06:25
поделиться

Не специально для разработки встраиваемых систем, но для этого мы использовали BoundsChecker .

0
ответ дан 6 December 2019 в 06:25
поделиться

Можете ли вы описать, что «не работает» с вашими методами ведения журнала?

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

Как вы подтвердили, что это определенно утечка, а не какой-то другой тип повреждения?

Один из способов проверить правильность вашей перегрузки: создать экземпляр счетчика объект на класс, увеличьте его в new и уменьшите его в delete класса. Если у вас растет счет, у вас утечка. Теперь вы ожидаете, что ваши строки журнала будут совпадать с точками увеличения и уменьшения.

0
ответ дан 6 December 2019 в 06:25
поделиться

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

Вы можете обнаруживать утечки в собственных структурах классов, добавляя счетчики памяти в вызовы New и Delete для увеличения / уменьшения счетчиков памяти , и распечатайте отчет при закрытии приложения. Однако при этом не будут обнаружены утечки памяти для памяти, выделенной вне системы классов - хотя это может сделать сторонний инструмент.

0
ответ дан 6 December 2019 в 06:25
поделиться

Если вы используете Linux, я предлагаю попробовать Valgrind .

8
ответ дан 6 December 2019 в 06:25
поделиться

Есть несколько форм оператора new:

void *operator new (size_t);
void *operator new [] (size_t);
void *operator new (size_t, void *);
void *operator new [] (size_t, void *);
void *operator new (size_t, /* parameters of your choosing! */);
void *operator new [] (size_t, /* parameters of your choosing! */);

Все вышеперечисленное может существовать как в глобальной области, так и в области класса. Для каждого оператора new существует эквивалентный оператор delete. Вам необходимо убедиться, что вы добавляете ведение журнала ко всем версиям оператора, если вы хотите это сделать именно так.

В идеале вы бы хотели, чтобы система вела себя одинаково независимо от того, присутствует ли запись в памяти или не. Например, библиотека времени выполнения MS VC выделяет больше памяти при отладке, чем в выпуске, потому что она ставит перед выделением памяти больший информационный блок и добавляет защитные блоки в начало и конец выделения. Лучшее решение - хранить всю информацию журнала памяти в виде отдельного фрагмента или памяти и использовать карту для отслеживания памяти. Это также можно использовать для проверки правильности памяти, переданной для удаления.

new
  allocate memory
  add entry to logging table

delete
  check address exists in logging table
  free memory

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

  1. Вы знаете, сколько имеется памяти, поэтому заранее знаете, сколько объектов вы можете выделить. Распределение никогда не должно возвращать null, поскольку это обычно является конечным, и нет простого способа вернуться к работоспособной системе.
  2. Выделение и освобождение памяти приводит к фрагментации. Количество объектов, которые вы можете выделить, со временем будет уменьшаться. Вы можете написать уплотнитель памяти, чтобы перемещать выделенные объекты, чтобы освободить большие куски памяти, но это повлияет на производительность. Как и в пункте 1, когда вы получаете ноль, все становится сложнее.

Итак, при выполнении встроенной работы вы обычно заранее знаете, сколько памяти можно выделить для различных объектов, и, зная это,

6
ответ дан 6 December 2019 в 06:25
поделиться

То, как мы сделали это с помощью нашего инструментария C 3D, заключалось в создании пользовательских макросов new / malloc и delete, которые регистрировали каждое выделение и освобождение файла. Конечно, мы должны были убедиться, что весь код вызывал наши макросы. Запись в файл журнала контролировалась флагом времени выполнения и происходила только при отладке, поэтому нам не приходилось перекомпилировать.

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

Это сильно сказалось на производительности, но нам нужно было сделать это только однажды.

1
ответ дан 6 December 2019 в 06:25
поделиться

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

Предполагая, что вы не можете использовать средства проверки динамической памяти, такие как инструмент с открытым исходным кодом valgrind Linux, инструменты статического анализа, такие как коммерческие продукты Coverity Prevent и Klocwork Insight , могут быть полезны. Я использовал все три и получил очень хорошие результаты со всеми из них.

1
ответ дан 6 December 2019 в 06:25
поделиться

Если вы переопределите конструктор и деструктор своих классов, вы можете печатать на экране или в файле журнала. Это даст вам представление о том, когда что-то создается, что создается, а также ту же информацию для удаления.

Для облегчения просмотра вы можете добавить временную глобальную переменную «INSTANCE_ID» и распечатать это (и увеличивать) при каждом вызове конструктора / деструктора. Затем вы можете просматривать по идентификатору, и это должно немного упростить работу.

1
ответ дан 6 December 2019 в 06:25
поделиться

http://www.linuxjournal.com/article/6059

На самом деле, исходя из моего опыта, всегда лучше создавать пулы памяти для встроенных систем и использовать собственный распределитель / де-распределитель. Мы легко можем определить утечки. Например, у нас был простой пользовательский менеджер памяти для vxworks, где мы сохраняем идентификатор задачи и временную метку в выделенном блоке памяти.

5
ответ дан 6 December 2019 в 06:25
поделиться

overloading new и delete должны работать, если вы внимательно следите за ним.

Может быть, вы можете показать нам, что не работает в этом подходе ?

1
ответ дан 6 December 2019 в 06:25
поделиться

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

В среде рабочего стола мне нравится работа DevPartner Studio. Это для Windows и проприетарный. Для Linux доступны бесплатные инструменты, но у меня нет опыта работы с ними. В качестве примера есть EFence


1
ответ дан 6 December 2019 в 06:25
поделиться

Множество хороших ответов.

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

1
ответ дан 6 December 2019 в 06:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: