C++: крошечная утечка памяти с помощью std::map

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

Я использую VC++2008 и команды _CrtMemCheckpoint и _CrtDumpMemoryLeaks для проверки утечек памяти.

Когда я анализирую любой файл, а затем удаляю его из памяти (наряду с любой другой заявленной памятью), я получаю 16-байтовую утечку памяти, которая выглядит следующим образом:

{290} normal block at 0x00486AF0, 16 bytes long.
Data: <  H `aH  hH  eH > C0 9A 48 00 60 61 48 00 18 68 48 00 D8 65 48 00

Мне удалось сузить «оскорбительную» строку кода до этого:

classDefinitions[FastStr(cString)] = classDef;

classDefinitions — это std::map<FastStr, FSLClassDefinition*> и частный член моего класса синтаксического анализатора.

FastStr — это простая «обертка» char*, позволяющая использовать простые c-строки в качестве значений ключа; у него нет утечек памяти (нет «новых» команд). 'FSLClassDefinition*', очевидно, является простым указателем на класс, так что здесь тоже нет ничего странного.

Теперь вот улов:

  1. эта строка выполняется много раз во время процесса синтаксического анализа, но я получаю утечку только одного 16-байтового блока.
  2. если я разбираю другой файл, нет еще одной 16-байтовой утечки памяти
  3. Если я удалю синтаксический анализатор из памяти (разместив его в блоке кода {}), затем воссоздам его в другом блоке кода и заставлю его проанализировать другой файл, то я получу второй 16-байтовый файл. утечка памяти.

Это заставляет меня подозревать утечку памяти в std::map; но это также может быть и моя ошибка... Я почти уверен, что это оскорбительная строка, потому что, если я остановлю синтаксический анализ перед, утечки памяти не будет; возможна утечка памяти, если я остановлю синтаксический анализ сразу после этой строки.

Кто-нибудь может это прокомментировать?


person Bill Kotsias    schedule 04.09.2009    source источник
comment
Известные последние слова: FastStr: у него нет утечек памяти   -  person Martin York    schedule 04.09.2009
comment
Я считаю маловероятным, что std::map имеет утечку памяти (если только вы не используете версию STL от какого-то малоизвестного поставщика, которая не была протестирована миллионами пользователей C++).   -  person Martin York    schedule 04.09.2009
comment
York: Ваш комментарий действительно сделал меня счастливым :-) Но правда в том, что в FastStr не было утечек памяти.   -  person Bill Kotsias    schedule 05.09.2009


Ответы (10)


«{290}» в отчете об утечке — это порядковый номер выделения памяти для блока памяти, в котором произошла утечка. Если этот порядковый номер всегда один и тот же, вы можете использовать _crtBreakAlloc, чтобы вызвать перерыв в отладчике при достижении этого порядкового номера выделения. Из трассировки стека вы можете узнать, где выделяется этот блок. Когда вы знаете, где и для какой цели он выделяется, довольно легко определить, почему он не освобождается.

Прочтите документацию по отладочной куче, чтобы узнать о _crtBreakAlloc.

person Community    schedule 04.09.2009

Уточним одну вещь: в std::map нет утечки. Код доступен для просмотра каждому разработчику, и он уже был бы обнаружен.

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

person rlbond    schedule 04.09.2009
comment
Согласитесь, что это маловероятно, но вы действительно не можете быть уверены, что на карте нет утечки, потому что он, возможно, нашел правильную комбинацию вещей, которая вызывает проблемы. Иногда бывают ошибки и в инструментах. - person San Jacinto; 04.09.2009
comment
Инструменты иногда имеют ошибки. Однако мы говорим о STL; он существовал всегда и используется таким количеством программистов на C++, что шансы, что ошибка такого масштаба существует в таком распространенном контейнере, как карта, очень малы. Слишком стройный. - person Faxwell Mingleton; 04.09.2009
comment
STL существует вечно, а ЭТА РЕАЛИЗАЦИЯ — нет. Это полуновый продукт, никогда не знаешь, какие изменения они внесли за кулисами. Опять же, я серьезно сомневаюсь, что это проблема STL, но такие вещи ДЕЙСТВИТЕЛЬНО случаются. - person San Jacinto; 04.09.2009

OldFart предоставил окончательное решение проблемы.

Утечек памяти изначально не было. Ячейки памяти, предложенные отладчиком, были в файле STL xmemory, строка 43 :

return ((_Ty _FARQ *)::operator new(_Count * sizeof (_Ty)));

Но это были не настоящие утечки памяти, а просто переусердствовавший отладчик VC++2008. Я протестировал PurifyPlus (eval.version) и предположил, что в моей программе нет утечек памяти. Итак, эти 16 байт удаляются при выходе из программы, хотя да, не очень хорошо, что STL не делает этого раньше.

Так что еще раз, спасибо всем за ваши ответы, во-первых, никогда не было проблем!

person Bill Kotsias    schedule 05.09.2009

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

person Bill    schedule 04.09.2009
comment
Какая это должна быть библиотека? Стандартная библиотека, конечно, этого не делает. Карта освобождает всю свою память, как только вызывается деструктор. - person jalf; 04.09.2009
comment
Motif, Qt, X11 и т. д. Обычной практикой является оптимизация по скорости за счет небольшого объема памяти. - person Bill; 04.09.2009

До сих пор мы не видели ни одной строки вашего кода, поэтому мы практически не можем сказать что-то большее, чем «во всех широко используемых реализациях STL std::map не происходит утечка памяти, при любых обстоятельствах." После запуска деструктора карты вся память освобождается.

Конечно, если вы используете какую-то непонятную проприетарную реализацию STL, все ставки сняты, но в остальном карта не виновата.

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

Но, скорее всего, проблема в FastStr или... в чем-то еще в вашем коде.

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

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

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

person jalf    schedule 04.09.2009

Если вы поместите какую-либо карту в область файлов в DLL, MS Visual Studio 2008 будет жаловаться на утечку памяти. Вам не нужно ничего делать с картой, просто объявите ее:

фунт-включить ‹карта›

использование пространства имен std;

карта ‹ короткая, длинная › test_map;

...

Обнаружены утечки памяти! Сброс объектов -> нормальный блок {143} по адресу 0x00037140, длина 24 байта. Данные: ‹@q @q @q > 40 71 03 00 40 71 03 00 40 71 03 00 CD CD CD CD Дамп объекта завершен.

Не происходит в основной программе; не происходит, если карта находится в области действия функции. Вероятно, это ложно: карта освобождается после прекращения обнаружения утечки памяти.

person Alex    schedule 03.03.2010

просто чтобы подтвердить, что статическая карта, расположенная в области файлов в DLL, создает ту же самую ситуацию с крошечной утечкой памяти (с использованием MS Visual Studio 2008).

person mox    schedule 02.08.2010

Моя интуиция в этом вопросе заключалась в том, чтобы взглянуть на FastStr. Вы говорите, что это простая оболочка char*, но как она справляется с копированием (т. е. копируется или воссоздается внутренний char*)? Можете ли вы показать нам код для FastStr, пожалуйста?

Кроме того, ваши доказательства, как указано, предполагают некоторые статические данные или если упомянутый вами «парсер» создан только один в ваших блоках тестового кода, то член объекта «парсер» является вероятным источником утечки памяти.

Другим предложением было бы запустить ваш код под более описательным инструментом, таким как valgrind, чтобы точно определить утечку? Valgrind (или Purify) сообщит вам точное место утечки памяти в коде.

person J.Churchill    schedule 04.09.2009
comment
Я не хотел бы показывать исходный код, потому что он большой (8 КБ) и, вероятно, запутает читателя. В нем нет «нового» оператора, и при копировании новый FastStr просто копирует исходный указатель на char*. Я проверил дважды и трижды. Кроме того, экземпляр FastStr имеет размер 20 байт, а не 16... - person Bill Kotsias; 04.09.2009
comment
Но если вы настаиваете на том, чтобы взглянуть на него, я опубликую его. - person Bill Kotsias; 04.09.2009
comment
Так как же FastStr приводит в порядок char*, который у него есть внутри? Я предполагаю, что он не удаляет его, и все же память должна быть где-то выделена ... выделяется ли char *, который FastStr обертывает кучу или стек? - person J.Churchill; 04.09.2009
comment
Символ FastStr* указывает где-то внутри файла, загруженного в память синтаксическим анализатором (с помощью std::ifstream). FastStr не удаляет [] символ. Парсер делает это, когда он закончен. - person Bill Kotsias; 04.09.2009
comment
Я только что установил ознакомительную версию PurifyPlus, и она сообщает об утечке памяти в 1 байт в 1 блоке при выходе из всей программы (включая синтаксический анализ 2 файлов и освобождение всей требуемой памяти). Итак, я полагаю, это то, что Билл сказал выше, библиотека освобождает память при выходе из программы..? - person Bill Kotsias; 04.09.2009

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

person Community    schedule 22.04.2019

Можно ли запустить этот код под Linux, используя «valgrind --leak-check»? Если это так, valgrind может показать вам, какая память утекает.

person Jeremy Friesner    schedule 04.09.2009