Помогите со странным поведением памяти. Ищите утечки как в моем мозгу, так и в моем коде - PullRequest
6 голосов
/ 21 декабря 2010

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

Прежде всего, я попытался использовать некоторые детекторы утечки.После исправления нескольких проблем они больше не обнаруживают утечек.Тем не менее, я также отслеживаю свое приложение, используя perfmon.exe.Монитор производительности сообщает, что «Частные байты» и «Рабочий набор - частный» неуклонно растут при использовании приложения.Для меня это говорит о том, что программа использует все больше и больше памяти, чем дольше она работает.Внутренние ресурсы кажутся стабильными, поэтому мне это кажется утечкой.

Программа загружает DLL во время выполнения.Я подозреваю, что эти утечки или что бы они ни происходили в этой библиотеке и очищаются, когда библиотека выгружается, следовательно, они не будут обнаружены детекторами утечек.Я использовал DevPartner BoundsChecker и Virtual Leak Detector для поиска утечек памяти.Обе предположительно ловят утечки в DLL.

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

Всякий раз, когда я сталкиваюсь с таким количеством странностей, я начинаюподвергнуть сомнению мои основные предположения.Поэтому я обращаюсь к вам, которые знают все, для предложений.Есть ли недостаток в моих предположениях?У вас есть идея, как решить проблему, подобную этой?

Редактировать:
В настоящее время я использую Microsoft Visual C ++ (x86) в Windows 7 64.

Edit2:
Я просто использовал IBM Purify для поиска утечек.Прежде всего, он перечисляет полные 30% программы как утечка памяти.Это не может быть правдой.Я предполагаю, что это идентифицирует всю DLL как утечку или что-то в этом роде.Однако, если я ищу новые утечки каждые несколько действий, он сообщает об утечках, которые соответствуют увеличению размера, сообщаемому Performance Monitor.Это может привести к утечке.К сожалению, я использую только пробную версию Purify, поэтому она не покажет мне фактическое местоположение этих утечек.(Эти утечки обнаруживаются только во время выполнения. При выходе из программы утечки вообще не сообщаются никаким инструментом.)

Ответы [ 5 ]

3 голосов
/ 21 декабря 2010

Мониторинг использования памяти приложением с помощью PerfMon или Task Manager - недопустимый способ проверки утечек памяти.Например, ваша среда выполнения может просто удерживать дополнительную память из ОС для целей предварительного выделения или из-за фрагментации.

По моему опыту, уловка - это отладочная куча CRT.Вы можете запросить информацию обо всех живых объектах, а CRT предоставляет функции для сравнения снимков.

http://msdn.microsoft.com/en-us/library/wc28wkas.aspx

3 голосов
/ 21 декабря 2010

Трудно понять, не видя ваш код, но есть менее очевидные способы, которые могут привести к "утечкам" в программе на C ++, например,

  1. фрагментация памяти - если вы выделяете разныеРазмер объектов постоянно, тогда иногда не будет достаточно большой непрерывной области свободной памяти, и от ОС придется выделять больше.Распределения, подобные этому, не будут возвращаться обратно в ОС до тех пор, пока не будет освобождена вся память в выделении, поэтому долго работающие программы будут иметь тенденцию расти (с точки зрения используемого адресного пространства) с течением времени.

  2. забыть иметь в базовом случае виртуальный объект с виртуальными функциями - очень распространенный метод, который приводит к утечкам.

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

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

Что касается инструментов, есть очищение, которое хорошо, но дорого.

2 голосов
/ 21 декабря 2010

Perfmon хорош для того, чтобы сообщить, что у вас течь, но он примитивен. Есть коммерческие продукты, которые будут работать намного лучше. Я использую AQTime для кода C ++, и это отлично: http://www.automatedqa.com/products/aqtime/

Он сообщит вам строку кода, которая распределила утечку памяти.

1 голос
/ 21 декабря 2010

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

В зависимости от глубины вашего знания кода, вы можете иметь в виду пару подозрительных мест.Что я делал в прошлом, так это нацеливался на эти подозрительные места (усиливая утечку).Это можно сделать, просто поместив цикл вокруг подозрительного места, чтобы он вызывался не один, а много раз, обычно тысячи, но это зависит от размера основного распределения.Хитрость заключается в том, чтобы знать, где поставить петлю.Как правило, вы хотите переместить стек вызовов в место, где в цикле ожидается освобождение всей выделенной памяти.Во время выполнения используйте perfmon для просмотра приватных байтов и рабочего набора, когда он достигает пика, вы обнаружили утечку.С этого момента вы можете сузить область цикла до стека вызовов до нуля при утечке.

Рассмотрим следующий пример (может показаться хромым):

char* leak()
{
    char* buf = new char[2];
    buf[0] = 'a';
    buf[1] = '\0';
}

char* furtherGetResults()
{
    return leak();
}

std::string getResults(const std::string& request)
{
    return furtherGetResults();
}

bool processRequest(SOCKET client, const std::string& request)
{    
    std::string results;

    results = getResults(request);

    return send(client, results.c_str(), results.length(), 0) == results.length();
}

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

Для начала вы можете поставить цикл вокруг вызова getResults ():

bool processRequest(SOCKET client, const std::string& request)
{    
    std::string results;

    for (size_t i = 0; i < 1000000; i++) {
        results = getResults(request);
    }

    return send(client, results.c_str(), results.length(), 0) == results.length();
}

Еслискачки использования памяти увеличивают утечку, после чего вы перемещаетесь вниз по стеку вызовов к getResults (), затем к nextGetResults () и так далее, пока не прибьете его.Этот пример чрезмерно упрощает технику, но в рабочем коде, как правило, в каждой вызываемой функции намного больше кода, и его труднее сузить.

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

1 голос
/ 21 декабря 2010

Perfmon смотрит на количество (4K) страниц, выделенных вашей программе. Те, как правило, управляются менеджером кучи. Например, если нажатие вашей кнопки требует 3 выделения по 1 КБ каждый, диспетчер кучи должен будет запросить новую страницу первые три раза. В четвертый раз осталось 3 КБ. Поэтому вы не можете сделать вывод, что нажатие вашей кнопки должно иметь внешне видимый эффект каждый раз.

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