Об управлении памятью, повреждении кучи и C ++

StackOverflow https://stackoverflow.com/questions/7525

  •  08-06-2019
  •  | 
  •  

Вопрос

Итак, мне нужна некоторая помощь.Я работаю над проектом на C ++.Тем не менее, я думаю, что мне каким-то образом удалось испортить свою кучу.Это основано на том факте, что я добавил std::string классу и присваивает ему значение из другого std::string:

std::string hello = "Hello, world.\n";
/* exampleString = "Hello, world.\n" would work fine. */
exampleString = hello;

сбой в моей системе с дампом стека.Так что в принципе мне нужно остановка и просмотрите весь мой код и материалы по управлению памятью и выясните, где я облажался.Кодовая база по-прежнему невелика (около 1000 строк), так что это легко выполнимо.

Тем не менее, я по горло сыт подобными вещами, так что я решил выбросить их вон.Я работаю в системе Linux и покопался с valgrind, и хотя я не знаю полностью, что я делаю, он сообщил, что std::stringдеструктор был недопустимо свободным.Я должен признаться, что получил термин "Повреждение кучи" из поиска Google;также были бы признательны любые статьи общего назначения по такого рода вопросам.

(В предыдущем rm -rf ProjectDir, сделайте еще раз на C # :D)

Редактировать:Я не объяснил это ясно, но то, о чем я прошу, - это советы по диагностике такого рода проблем с памятью.Я знаю, что std::string все правильно, так что это то, что я сделал (или ошибка, но с Select проблем нет).Я уверен, что мог бы проверить написанный мной код, и вы, очень умные люди, моментально увидели бы проблему, но я хочу добавить этот вид анализа кода, так сказать, в свой "инструментарий".

Это было полезно?

Решение

Это относительно дешевые механизмы возможного решения проблемы:

  1. Присматривай за моим вопрос о повреждении кучи - Я буду обновлять ответы по мере их поступления.Первым было уравновешивание new[] и delete[], но ты уже делаешь это.
  2. Отдавать валгринд больше похоже на попытку;это отличный инструмент, и я только хотел бы, чтобы он был доступен под Windows.I только замедляет работу вашей программы примерно наполовину, что довольно хорошо по сравнению с аналогами Windows.
  3. Подумайте об использовании Инструменты повышения производительности Google в качестве замены malloc/новый.
  4. Вы очистили все свои объектные файлы и начали все сначала?Возможно, ваш make-файл таков..."неоптимальный"
  5. Ты не такой assert()этого достаточно в вашем коде.Откуда мне это знать, если я этого не видел?Как пользоваться зубной нитью, никто assert()этого достаточно в их коде.Добавьте функцию проверки для ваших объектов и вызывайте ее при запуске метода и завершении метода.
  6. Это ты компиляция -стена?Если нет, сделайте это.
  7. Найдите себе такой инструмент для удаления ворса, как PC-Ворсинка.Небольшое приложение, подобное вашему, могло бы поместиться в Демо-версия PC-lint страница, что означает отсутствие покупки для вас!
  8. Убедитесь, что вы обнуляете указатели после их удаления.Никому не нравится болтающаяся указка.Тот же концерт с объявленными, но нераспределенными указателями.
  9. Прекратите использовать массивы.Используйте вектор вместо этого.
  10. Не используйте необработанные указатели.Используйте умный указатель.Не используйте auto_ptr!Эта штука такова...удивительно;его семантика очень странная.Вместо этого выберите один из следующих вариантов Улучшайте интеллектуальные указатели, или что - то из библиотека Локи.

Другие советы

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

В конце концов мы отследили это, используя контрольные точки отладчика.Я попытаюсь описать эту процедуру здесь:

1) Найдите причину сбоя.Из вашего примера кода видно, что память для "exampleString" повреждена, и поэтому в нее невозможно записать.Давайте продолжим с этим предположением.

2) Установите точку останова в последнем известном местоположении, в котором "exampleString" используется или изменяется без каких-либо проблем.

3) Добавьте точку наблюдения к элементу данных 'exampleString'.В моей версии g ++ строка хранится в _M_dataplus._M_p.Мы хотим знать, когда изменится этот элемент данных.Метод GDB для этого заключается в следующем:

(gdb) p &exampleString._M_dataplus._M_p
$3 = (char **) 0xbfccc2d8
(gdb)  watch *$3
Hardware watchpoint 1: *$3

Очевидно, что здесь я использую linux с g ++ и gdb, но я считаю, что точки наблюдения за памятью доступны в большинстве отладчиков.

4) Продолжайте до тех пор, пока не сработает контрольная точка:

Continuing.
Hardware watchpoint 2: *$3

Old value = 0xb7ec2604 ""
New value = 0x804a014 ""
0xb7e70a1c in std::string::_M_mutate () from /usr/lib/libstdc++.so.6
(gdb) where

gdb where команда выдаст обратную трассировку, показывающую, что привело к модификации.Это либо совершенно законная модификация, и в этом случае просто продолжайте - или, если вам повезет, это будет модификация из-за повреждения памяти.В последнем случае теперь вы должны иметь возможность просмотреть код, который в самом деле вызывающий проблему и, надеюсь, исправляющий ее.

Причиной нашей ошибки был доступ к массиву с отрицательным индексом.Индекс был результатом приведения указателя на модуль 'int' размером с массив.Ошибка была пропущена valgrind и др.поскольку адреса памяти, выделяемые при запуске под этими инструментами, никогда не были "> MAX_INT" и это никогда не приводило к отрицательному индексу.

О, если вы хотите знать, как устранить проблему, это просто.Сначала возьмите мертвую курицу.Тогда, начинай его трясти.

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

  1. Устраивайтесь поудобнее в отладчике.
  2. Начните копаться в отладчике, чтобы посмотреть, сможете ли вы найти что-нибудь подозрительное.Проверьте специально, чтобы увидеть, что происходит во время exampleString = hello; линия.
  3. Проверьте, чтобы убедиться, что это действительно сбой на exampleString = hello; строка, а не при выходе из какого-либо окружающего блока (что может привести к срабатыванию деструкторов).
  4. Проверьте любую магию указателя, которую вы, возможно, используете.Арифметика указателей, приведение и т.д.
  5. Проверьте все ваши распределения и освобождения, чтобы убедиться, что они совпадают (никаких двойных освобождений).
  6. Убедитесь, что вы не возвращаете никаких ссылок или указателей на объекты в стеке.

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

С чего можно начать:

Если вы работаете в Windows и используете visual C ++ 6 (я молю бога, чтобы никто до сих пор не использовал его в наши дни), то внедрение std::string не является потокобезопасным и может привести к такого рода вещам.

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

На моем предыдущем рабочем месте мы использовали Compuware Boundschecker, чтобы помочь с этим.Это коммерческое и очень дорогое решение, так что, возможно, это не вариант.

Вот пара бесплатных библиотек, которые могут оказаться полезными

http://www.codeguru.com/cpp/misc/misc/memory/article.php/c3745/

http://www.codeproject.com/KB/cpp/MemLeakDetect.aspx

Надеюсь, это поможет.Повреждение памяти - это отстойное место, в котором нужно находиться!

Это может быть повреждение кучи, но с такой же вероятностью это повреждение стека.Джим прав.Нам действительно нужно немного больше контекста.Эти две строки исходного кода сами по себе мало что нам говорят.Причиной этого может быть любое количество причин (что является настоящей радостью C / C ++).

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

Код был просто примером того, где моя программа давала сбой (он был размещен в стеке, Джим).На самом деле я ищу не "что я сделал не так", а скорее "как мне диагностировать, что я сделал не так".Научи человека ловить рыбу и все такое.Хотя, глядя на этот вопрос, я недостаточно ясно выразился.Слава богу, есть функция редактирования.:')

Кроме того, я фактически исправил проблему std::string.Каким образом?Заменив его вектором, скомпилировав, затем снова заменив строку.IT был постоянно происходил сбой, и это было исправлено, хотя ... не могло.Там есть что-то неприятное, и я не уверен, что именно.Однако я хотел проверить тот единственный раз, когда я вручную выделял память в куче:

 this->map = new Area*[largestY + 1];
 for (int i = 0; i < largestY + 1; i++) {
     this->map[i] = new Area[largestX + 1];
 }

и удаляю его:

for (int i = 0; i < largestY + 1; i++) {
    delete [] this->map[i];
}
delete [] this->map;

Я раньше не выделял 2d-массив с помощью C ++.Кажется, это работает.

Кроме того, я фактически исправил проблему std::string.Каким образом?Заменив его вектором, скомпилировав, затем снова заменив строку.Там постоянно происходил сбой, и это было исправлено, хотя ... не могло.Там есть что-то неприятное, и я не уверен, что именно.

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

Запустите программу Purify.

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

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

Одна из самых приятных конференций поставщиков, на которой я когда-либо присутствовал, была, когда Purify обнаружила утечку памяти в их коде, и мы смогли спросить: "Возможно ли, что вы не освобождаете память в своей функции foo()", и услышать удивление в их голосах.

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

http://www-306.ibm.com/software/awdtools/purify/unix/

(Это довольно дорого, но у них есть бесплатная загрузка eval)

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

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

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

Помимо таких инструментов, как Boundschecker или Purify, ваш лучший выбор при решении подобных проблем - просто научиться действительно хорошо читать код и ознакомиться с кодом, над которым вы работаете.

Повреждение памяти - одна из самых сложных проблем для устранения, и обычно проблемы такого типа решаются путем проведения часов / дней в отладчике и обнаружения чего-то вроде "эй, указатель X используется после того, как он был удален!".

Если это кому-то и помогает, то это то, в чем вы становитесь лучше по мере накопления опыта.

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

Ваш код, насколько я вижу, не содержит ошибок.Как уже было сказано, требуется больше контекста.

Если вы еще не пробовали, установите gdb (отладчик gcc) и скомпилируйте программу с помощью -g .Это скомпилирует в отладочные символы, которые может использовать gdb.После установки gdb запустите его с помощью программы (gdb ). Это это полезная программа для использования gdb.

Установите точку останова для функции, которая создает ошибку, и посмотрите, каково значение exampleString .Также сделайте то же самое для любого параметра, который вы передаете в exampleString.Это должно, по крайней мере, сказать вам, являются ли std::strings допустимыми.

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

Насколько я могу судить, ваш код верен.Предполагая, что exampleString - это std::string, который имеет область видимости класса, как вы описали, вы должны быть в состоянии инициализировать / назначить его таким образом.Возможно, есть какая-то другая проблема?Возможно, фрагмент реального кода помог бы поместить это в контекст.

Вопрос:Является ли exampleString указателем на строковый объект, созданный с помощью new ?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top