Подтвердить что ты не робот

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

Итак, мне нужна помощь. Я работаю над проектом на С++. Однако, я думаю, мне удалось каким-то образом развратить мою кучу. Это связано с тем, что я добавил класс 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; любые статьи общего назначения по подобным вещам также будут оценены.

(In до rm -rf ProjectDir, повторите попытку в С#: D)

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

4b9b3361

Ответ 1

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

  • Следите за моим вопросом кукурузного разложения - я обновляюсь с ответами, когда они вытряхиваются. Первая была балансировка new[] и delete[], но вы уже это делаете.
  • Дайте valgrind больше; это отличный инструмент, и я только хочу, чтобы он был доступен под Windows. Я только замедляю вашу программу примерно на половину, что довольно хорошо по сравнению с эквивалентами Windows.
  • Подумайте об использовании Инструментов Google Performance в качестве замены malloc/new.
  • Вы очистили все свои объектные файлы и начали? Возможно, ваш файл make... "suboptimal"
  • В коде вы недостаточно assert() ing. Откуда я знаю, что, не увидев этого? Подобно flossing, no-one assert() достаточно в их коде. Добавьте функцию проверки для своих объектов и вызовите ее при запуске метода и конце метода.
  • Вы compiling -wall? Если нет, сделайте это.
  • Найдите инструмент lint, например PC-Lint. Небольшое приложение, подобное вашему, может поместиться на странице PC-lint demo, что означает отсутствие покупки для вас!
  • Убедитесь, что вы удалили указатели после их удаления. Никто не любит свисающий указатель. Тот же концерт с объявленными, но нераспределенными указателями.
  • Прекратить использование массивов. Вместо этого используйте vector.
  • Не используйте необработанные указатели. Используйте умный указатель. Не используйте auto_ptr! Эта вещь... удивительна; его семантика очень странная. Вместо этого выберите один из Boost smart pointers или что-то из библиотека Loki.

Ответ 2

У нас когда-то была ошибка, которая ускользала из всех обычных методов, valgrind, очистила и т.д. Авария произошла только на машинах с большим объемом памяти и только на больших наборах входных данных.

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

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 et al. поскольку адреса памяти, выделенные при работе под этими инструментами, никогда не были "> MAX_INT" и поэтому никогда не приводили к отрицательному индексу.

Ответ 3

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

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

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

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

Ответ 4

Некоторые места для запуска:

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

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

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

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

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

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

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

Ответ 5

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

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

Ответ 6

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

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

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

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

Ответ 7

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

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

 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-массив с С++. Кажется, что это работает.

Ответ 8

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

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

Ответ 9

Запустить очистку.

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

Он работает на уровне машинного кода, поэтому вам даже не нужно иметь исходный код.

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

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

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

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

Ответ 10

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

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

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

Ответ 11

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

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

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

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

Ответ 12

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

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